{{ message }}
Repository navigation
Conversation
🤖 I have created a release *beep* *boop* --- ## [0.3.25](a2aproject/a2a-python@v0.3.24...v0.3.25) (2026-03-10) ### Features * Implement a vertex based task store ([a2aproject#752](a2aproject#752)) ([fa14dbf](a2aproject@fa14dbf)) ### Bug Fixes * return background task from consume_and_break_on_interrupt to prevent GC ([a2aproject#775](a2aproject#775)) ([a236d4d](a2aproject@a236d4d)) * use default_factory for mutable field defaults in ServerCallContext ([a2aproject#744](a2aproject#744)) ([22b25d6](a2aproject@22b25d6)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
# Description This PR a2aproject/A2A#1507 introduces a breaking change in the specification which is going to be addressed separately.
…2aproject#795) * Unified v0.3 Handling (grpc_handler.py): Refactored the CompatGrpcHandler to delegate directly to RequestHandler03, removing ad-hoc data transformations and unifying translation logic across all transports. * REST & JSONRPC Adapters: Implemented and tested legacy HTTP adapters. Added comprehensive tests in test_rest_handler.py and test_rest_fastapi_app_compat.py to validate exact JSON payload shapes and legacy routing (e.g., /v0.3/v1/message:send). * Feature Flag Integration: Introduced the enable_v0_3_compat flag for A2AStarletteApplication and A2ARESTFastAPIApplication. When disabled, legacy requests are securely rejected with standard errors to prevent overhead. * Cross-Version Integration: Upgraded the v1.0 reference test server (server_1_0.py) to announce and mount legacy REST interfaces. The automated test matrix now explicitly validates jsonrpc and rest paths alongside grpc. Fixes a2aproject#742
Source: [11.5. Query Parameter Naming for Request Parameters](https://a2a-protocol.org/latest/specification/#115-query-parameter-naming-for-request-parameters): > **Field Type Handling:** > > - **Strings**: Passed directly as query parameter values > - **Booleans**: Represented as lowercase strings (`true`, `false`) > - **Numbers**: Represented as decimal strings > - **Enums**: Represented using their string values (e.g., `status=working`) > - **Repeated Fields**: Multiple values **MAY** be passed by repeating the parameter name (e.g., `?tag=value1&tag=value2`) or as comma-separated values (e.g., `?tag=value1,value2`) > - **Nested Objects**: Not supported in query parameters; operations requiring nested objects **MUST** use POST with a request body > - **Datetimes/Timestamps**: Represented as ISO 8601 strings (e.g., `2025-11-09T10:30:00Z`) 1. Using `MessageToDict` in combination with `httpx` client produces correct output (test is added). 2. Parsing has an extra addition on top of `ParseDict` to support repeated fields in both formats (`?tag=value1&tag=value2` and `?tag=value1,value2`). 3. `test_client_server_integration.py` is updated to dedupe gRPC and HTTP based tests and it also asserts params now.
…roject#783) ## Description Implements a mechanism to handle legacy v0.3 data stored in the database. When a `Task` or a `TaskPushNotificationConfig` does not have a `protocol_version` set to `0.1`, it validates and converts the data using v0.3 Pydantic models and conversion utilities. This ensures backward compatibility for existing records containing string-based enums and old field structures. ## Changes - The `status`, `artifacts`, and `history` fields in `TaskMixin` now use standard SQLAlchemy JSON columns with explicit Python type hints (Mapped[Any], Mapped[list[Any]]). - Removed `PydanticType` and `PydanticListType`: These custom SQLAlchemy types are no longer needed as serialization is now handled at the Store level. - Updated `_to_orm` to use MessageToDict on the entire Task object - Updated `_from_orm`: - v1.0: Uses ParseDict - Legacy (v0.3): Uses Pydantic's model_validate to reconstruct the legacy Task tree before converting to core types. - Updated Tests: - Removed obsolete tests for the deleted Pydantic type - updated the Task Store integration tests to verify the new `0.3 type to 1.0 type` conversion logic ## Contributing Guide - [x] Follow the [`CONTRIBUTING` Guide](https://github.com/a2aproject/a2a-python/blob/main/CONTRIBUTING.md). - [x] Make your Pull Request title in the <https://www.conventionalcommits.org/> 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 and linter pass (Run `bash scripts/format.sh` from the repository root to format) - [x] Appropriate docs were updated (if necessary) Fixes a2aproject#715 🦕
Add tests and update REST error handler to catch `ParseError` thrown by ProtoJSON, currently it produces 500.
# Description This PR introduces a check for the jsonrpc version, which for tck tests and [specifications](https://a2a-protocol.org/latest/specification/#91-protocol-requirements) must be 2.0.
1. The script invokes Python linters from the CI, JSCPD is not invoked. 2. GitHub action is updated to run pyright using local env to match versions and make results reproduciable. <img width="870" height="564" alt="image" src="https://github.com/user-attachments/assets/ece92c55-b1c3-4475-9cc7-2b9b93cc99f1" />
To make tests a bit higher level and closer to how it's used externally.
…ly) (a2aproject#810) Original spec PR: a2aproject/A2A#1507. Required a bunch of inversions including compat conversion updates. The gotcha here is that 0.3 SDK **already** uses blocking mode by default (although it's not according to the spec), so this spec change is about applying a de-facto standard.
…2aproject#813) Removed redundant type casting in database_task_store.py.
1. Run release-please on 1.0-dev and set `target-branch` in the GitHub Action. 2. Use advanced configuration to specify pre-release versioning (`"prerelease": true` and `"prerelease-type": "alpha"`), this is going to result in `1.0.0a0` (see [here](https://packaging.python.org/en/latest/specifications/version-specifiers/#pre-releases) package for `v1.0.0-alpha.0` tag. 3. [python-publish.yml](https://github.com/a2aproject/a2a-python/blob/main/.github/workflows/python-publish.yml) workflow was disabled as a safety measure in case release-please goes crazy with this config. For now do not specify `last-release-sha` explicitly ([docs](https://github.com/googleapis/release-please/blob/main/docs/manifest-releaser.md#configfile)), will check the behavior first, changelog will have to be created manually most likely either way.
Release-As: v1.0.0-alpha.0
From ```bash git log origin/main..1.0-dev --oneline --reverse | head -n 1 ```
…aproject#812) # Description Old version is not able to write in databases which are `1.0` compatible because the new `owner` field is mandatory. This change makes the `owner` field `optional` to maintain forward compatibility. ## Tested ### Before change (owner field mandatory) I created a database with tables `tasks` and `push_notification_configs` using `1.0` spec. I then tested it against `0.3` spec by trying to **write** in it. It failed returning an error: `NOT NULL constraint failed: tasks.owner`. Also, I created a database with `0.3` spec `Task` and `PushNotificationConfigs` entries but then made it `1.0` compatible via migration CLI command `uv run a2a-db`. **Reading** such data using `0.3` spec worked, but **writing** failed for the same reason as before. ### After change (owner field optional) I repeated the testing from before. Now writing in `1.0` compatible databases worked. ## Notes Writing in database which was created using `1.0` spec without running the migration `a2a-db` CLI command, created entries which have `owner=NULL` values, contrary to databases that underwent the migration using `a2a-db` which have `owner` field populated with the default value. In my case with `legacy_v03_no_user_info` since I didn't use the `--add_columns_owner_last_updated-default-owner` flag to pass my own custom default value. ## Contributing Guide - [x] Follow the [`CONTRIBUTING` Guide](https://github.com/a2aproject/a2a-python/blob/main/CONTRIBUTING.md). - [x] Make your Pull Request title in the <https://www.conventionalcommits.org/> 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 and linter pass (Run `bash scripts/format.sh` from the repository root to format) - [x] Appropriate docs were updated (if necessary) Fixes a2aproject#811 🦕
……s`, `artifacts`, and `history`. (a2aproject#819) This is a rollback of a change made in: a2aproject#783
…a2aproject#814) # Description This PR addresses two main issues related to the list_tasks JSON-RPC endpoint: - Fixed how list_tasks formats the ListTasksResponse to ensure all fields are explicitly printed, even when they carry default values (such as an empty next_page_token). - Replaced generic ValueError exceptions with A2A SDK's standard InvalidParamsError when handling malformed or invalid pagination tokens. This correctly surfaces -32602 Invalid params via JSON-RPC.
…ts (a2aproject#820) - Removed the `/v0.3` prefix from v0.3 compatibility REST routes in `A2ARESTFastAPIApplication`, unifying the URL routing structure. - Expanded cross-version integration tests (`client_0_3`, `client_1_0`, `server_0_3`, `server_1_0`) to validate complex message parts, including URIs, raw bytes, and structured data parts. - Added cross-version test coverage for the push notification config lifecycle (create, get, list, delete) and task listing. - Improved subprocess cleanup and lifecycle management in `test_client_server.py` using process groups (`os.killpg`) to prevent dangling server processes on timeout. - Added a custom logging middleware for easier debugging during cross-version integration tests.
…#799) (a2aproject#800) Made a minor change to A2ACardResolver.get_agent_card() so it doesn't introduce an spurious trailing slash when agent_card_path is empty. This allows one to get the card from a card URL without having to break it into a base and relative card path component, it's arguably a bit odd but the empty agent_card_path is technically allowed and the result of that isn't right so I think it's a reasonable improvement, and one that as far as I can see can't reasonably be not backwards compatible for anyone. I changed one existing test, but only in a fashion such that it still tests the thing that it says it is testing... It is fine IF I understood the point of the 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](https://github.com/a2aproject/a2a-python/blob/main/CONTRIBUTING.md). - [X] Make your Pull Request title in the <https://www.conventionalcommits.org/> 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 and linter pass (Run `bash scripts/format.sh` from the repository root to format) - [X] Appropriate docs were updated (if necessary) Fixes a2aproject#799 --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: Iva Sokolaj <102302011+sokoliva@users.noreply.github.com>
…#790) # Description This PR implements standard gRPC rich error handling using `google.rpc.Status` and `google.rpc.ErrorInfo`, bringing the SDK's gRPC transport fully in line with the A2A v1.0 specification. Previously, the gRPC server appended the exception name to the string message (e.g., "TaskNotFoundError: task not found"), and the client relied on string splitting to parse the error back into a domain exception. This approach was brittle and not interoperable with standard gRPC ecosystems (proxies, gateways, etc.). This PR replaces the legacy string-parsing heuristic entirely with strongly-typed binary metadata (`grpc-status-details-bin`). - [X] Follow the [`CONTRIBUTING` Guide](https://github.com/a2aproject/a2a-python/blob/main/CONTRIBUTING.md). - [X] Make your Pull Request title in the <https://www.conventionalcommits.org/> 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 and linter pass (Run `bash scripts/format.sh` from the repository root to format) - [X] Appropriate docs were updated (if necessary) Fixes a2aproject#723 🦕 --------- Co-authored-by: Ivan Shymko <ishymko@google.com>
…a2aproject#821) # Description Fix the database PushNotificationConfig store when reading legacy data from version 0.3. The store now correctly handles cases when legacy PushNotificationConfig was stored.
It's not fully transparent for non-release versions (like alpha or rc) how git tag names (i.e. `v1.0.0-alpha.0`) are converted to [Python version specifiers](https://packaging.python.org/en/latest/specifications/version-specifiers/#version-specifiers) (i.e. `1.0.0a0`). This can be checked only during [publish GitHub action](https://github.com/a2aproject/a2a-python/actions/workflows/python-publish.yml) execution which both builds and pushes PyPi package. `release-please` supports updating `pyproject.toml` so we will have the same level of automation. Current version is set to `0.3.25` - the latest release from `main`.
…2aproject#827) Turns out it creates more problems than solves and requires more intervention into release-please to also update `uv.lock`. release-please still uses SemVer values in its PRs, but they are normalized during build (see [here](https://packaging.python.org/en/latest/specifications/version-specifiers/#normalization)).
🤖 I have created a release *beep* *boop* --- ## [1.1.1](a2aproject/a2a-python@v1.1.0...v1.1.1) (2026-07-15) ### Bug Fixes * **server:** improve Swagger /docs example annotations for A2A routes ([a2aproject#1092](a2aproject#1092)) ([46b292b](a2aproject@46b292b)) ### Miscellaneous Chores * **grpc:** eliminate third-party grpcio-status dependency ([a2aproject#1130](a2aproject#1130)) ([723880e](a2aproject@723880e)) * **http:** eliminate httpx-sse dependency ([a2aproject#1132](a2aproject#1132)) ([0d82ab9](a2aproject@0d82ab9)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: Iwaniukooo11 <mateusz.iwaniuk11@gmail.com>
a2aproject#1106) Fixes a2aproject#1067 ## Summary - seed the originating message into V2 ActiveTask TaskManager creation so newly created fallback tasks retain request history - synchronously persist TASK_STATE_FAILED from _run_producer when producer setup/execution raises, so persistence does not depend on the closing event queue - avoid appending the seeded originating message a second time when the first task update also carries it ## Tests - ./scripts/lint.sh - uv run pytest tests/server/request_handlers/test_default_request_handler_v2.py::test_on_message_send_early_producer_exception_marks_task_failed tests/server/request_handlers/test_default_request_handler_v2.py::test_on_message_send_early_producer_exception_preserves_originating_message - uv run pytest tests/server/request_handlers/test_default_request_handler_v2.py --------- Co-authored-by: mykytanetipa <mykytanetipa@google.com>
…ing during teardown (a2aproject#1122) Fixes a2aproject#1121. This PR resolves the issue where `ActiveTask` would throw an asyncio warning when being garbage collected, by ensuring the `_producer_task` is properly awaited during the `_run_consumer` teardown path, allowing queues to properly close without leaving dangling pending background tasks. --------- Co-authored-by: mykytanetipa <mykytanetipa@google.com>
…roject#1101) (a2aproject#1105) ## Summary Adds a public `aclose()` teardown to `ActiveTask`, `ActiveTaskRegistry`, and `DefaultRequestHandlerV2` that force-drains the SDK-owned producer, consumer, and dispatcher `asyncio.Task`s so none are left pending at event-loop shutdown. - `ActiveTask.aclose()` force-closes both event queues and cancels the producer and consumer tasks, then awaits them. It sets `_is_finished` under `_lock`, so it is mutually exclusive with `start()` (which refuses to spawn once `_is_finished` is set). - `ActiveTaskRegistry.aclose()` marks the registry closed so `get_or_create` refuses new work, drains every active task, then awaits the in-flight `_remove_task` cleanup tasks. The lock is released before awaiting because `_remove_task` re-acquires it. - `DefaultRequestHandlerV2.aclose()` delegates to the registry drain, for wiring into an ASGI `lifespan` / `on_shutdown` hook. ## Why Fixes a2aproject#1101. At shutdown the `ActiveTask` producer can stay pending and surface as `Task was destroyed but it is pending!`. The producer's `finally` calls `_event_queue_subscribers.close(immediate=False)`, which awaits `join()` on every subscriber sink; an abandoned subscriber leaves an undrained sink, so the `join()` never returns and the producer hangs. There is no public way to drain these background tasks today. `aclose()` closes the queues with `immediate=True`, which releases the wedged producer, and reaps the tasks. The teardown always forces rather than exposing a graceful `immediate=False` option, because that path inherits the documented `close(immediate=False)` deadlock and a shutdown hook must be bounded. ## Test plan - [x] `uv run pytest tests/server/agent_execution/ tests/server/request_handlers/ tests/server/events/` — pass - [x] `uv run pytest --cov=a2a --cov-fail-under=88` — pass - [x] `./scripts/lint.sh` — ruff, ruff-format, and ty clean New tests cover the registry drain, idempotency, empty registry, new-work rejected after close, and an errored task being logged rather than propagated; `ActiveTask` reaping a running producer and force-closing past an undrained subscriber (the a2aproject#1101 repro); and the handler drain. Fixes a2aproject#1101 🦕
🤖 I have created a release *beep* *boop* --- ## [1.1.2](a2aproject/a2a-python@v1.1.1...v1.1.2) (2026-07-20) ### Features * **server:** add aclose() to drain ActiveTask background tasks ([a2aproject#1101](a2aproject#1101)) ([a2aproject#1105](a2aproject#1105)) ([9801f46](a2aproject@9801f46)) ### Bug Fixes * **agent_execution:** resolve ActiveTask 'destroyed but pending' warning during teardown ([a2aproject#1122](a2aproject#1122)) ([d19c4d2](a2aproject@d19c4d2)) * persist early producer failure as FAILED with originating message ([a2aproject#1106](a2aproject#1106)) ([4e3d724](a2aproject@4e3d724)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
Safety net pipeline to protect from accidental release creation which recognizes `release-please` PRs based on the `a2a-bot` author and `release-please--` branch name prefix and requires **2 approvers** with at least `write` level access. This is **an addition** on top of built-in GitHub write access check. Release PRs could never be merged by non-maintainers, but now the requirement is 2 maintainers. Published via a commit status instead of a workflow failure to avoid "spam" emails of workflow failures, this check will be added as mandatory. For non-release PRs passes immediately. Tested on fork: 1. "release" PR: ishymko#7 <img width="784" height="94" alt="image" src="https://github.com/user-attachments/assets/8fa922d5-cd04-46ca-a0ca-1de099fd7a46" /> 2. "normal" PR: ishymko#6 <img width="678" height="102" alt="image" src="https://github.com/user-attachments/assets/e7ef3242-9aa6-454c-bf53-d6d1acddec1f" />
Follow-up for a2aproject#1144. Release please PRs are never created from forks, so running on `pull_request_review` (which has only readonly access for PRs from forks) will never need to post a commit status. Such cases are skipped now instead of failing (i.e. [here](https://github.com/a2aproject/a2a-js/actions/runs/30247915633/job/89919011242?pr=602)).
…1155) # Description Adds itk/uv.lock so the ITK agent resolves the same dependency set every run. Inert until [the harness passes --locked](a2aproject/a2a-itk#27).
Bumps [pyasn1](https://github.com/pyasn1/pyasn1) from 0.6.3 to 0.6.4. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/pyasn1/pyasn1/releases">pyasn1's releases</a>.</em></p> <blockquote> <h2>Release 0.6.4</h2> <p>This is a security release.</p> <ul> <li>CVE-2026-59885 (GHSA-8ppf-4f7h-5ppj): Fixed quadratic time complexity in the OBJECT IDENTIFIER and RELATIVE-OID decoders. A small crafted substrate encoding many arcs could consume excessive CPU.</li> <li>CVE-2026-59884 (GHSA-m4p7-r5rc-7g4j): Limited BER long-form tag IDs to 20 octets (140 bits). Unbounded tag IDs allowed a crafted substrate to consume excessive CPU and memory.</li> <li>CVE-2026-59886 (GHSA-hm4w-wwcw-mr6r): Fixed excessive memory and CPU consumption in <code>Real.__float__()</code> for values with large base-10 exponents.</li> <li>Pinned PyPI publish GitHub Action to an immutable commit.</li> </ul> <p>All changes are noted in the <a href="https://github.com/pyasn1/pyasn1/blob/main/CHANGES.rst">CHANGELOG</a>.</p> </blockquote> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/pyasn1/pyasn1/blob/main/CHANGES.rst">pyasn1's changelog</a>.</em></p> <blockquote> <h2>Revision 0.6.4, released 08-07-2026</h2> <ul> <li>CVE-2026-59885 (GHSA-8ppf-4f7h-5ppj): Fixed quadratic time complexity in the OBJECT IDENTIFIER and RELATIVE-OID decoders. A small crafted substrate encoding many arcs could consume excessive CPU. Arcs are now accumulated in linear time; decoded values are unchanged (thanks for reporting, tynus2)</li> <li>CVE-2026-59884 (GHSA-m4p7-r5rc-7g4j): Limited BER long-form tag IDs to 20 octets (140 bits), matching the OID arc limit introduced in 0.6.2. Unbounded tag IDs allowed a crafted substrate to consume excessive CPU and memory; longer tag IDs are now rejected with PyAsn1Error. Also fixed Tag and TagSet repr() failing on huge tag (thanks for reporting, mikeappsec) IDs due to the integer-to-string conversion limit (Python 3.11+)</li> <li>CVE-2026-59886 (GHSA-hm4w-wwcw-mr6r): Fixed excessive memory and CPU consumption in Real.<strong>float</strong>() for values with large base-10 exponents. Conversion no longer materializes huge intermediate integers; values too large to represent as a Python float raise OverflowError promptly, and prettyPrint() renders them as '<!-- raw HTML omitted -->' as before. Also fixed base-10 mantissa normalization to use exact integer arithmetic; mantissas larger than 2**53 could previously lose precision through float division (thanks for reporting, gvozdila)</li> <li>Pinned PyPI publish GitHub Action to an immutable commit [pr <a href="https://redirect.github.com/pyasn1/pyasn1/issues/113">#113</a>](<a href="https://redirect.github.com/pyasn1/pyasn1/pull/113">pyasn1/pyasn1#113</a>)</li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/pyasn1/pyasn1/commit/72e4803405816c371ed3b2cb4be181c47f038406"><code>72e4803</code></a> Prepare release 0.6.4</li> <li><a href="https://github.com/pyasn1/pyasn1/commit/0c19eeb853731db1c717ff125ea001a1e558332d"><code>0c19eeb</code></a> Pin PyPI publish action to immutable commit (<a href="https://redirect.github.com/pyasn1/pyasn1/issues/113">#113</a>)</li> <li><a href="https://github.com/pyasn1/pyasn1/commit/45bdb19eb7df4b3780fe9c912c63e99bffc39dd9"><code>45bdb19</code></a> Merge commit from fork</li> <li><a href="https://github.com/pyasn1/pyasn1/commit/628e36ecbb5277a3f01572ce418ef54271b165a5"><code>628e36e</code></a> Merge commit from fork</li> <li><a href="https://github.com/pyasn1/pyasn1/commit/e60c691cb91addb8fcefa2f537e85ede6fb1e886"><code>e60c691</code></a> Merge commit from fork</li> <li>See full diff in <a href="https://github.com/pyasn1/pyasn1/compare/v0.6.3...v0.6.4">compare view</a></li> </ul> </details> <br /> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/a2aproject/a2a-python/network/alerts). </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Iva Sokolaj <102302011+sokoliva@users.noreply.github.com>
Bumps [setuptools](https://github.com/pypa/setuptools) from 82.0.1 to 83.0.0. <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/pypa/setuptools/blob/main/NEWS.rst">setuptools's changelog</a>.</em></p> <blockquote> <h1>v83.0.0</h1> <h2>Features</h2> <ul> <li>Require Python 3.10 or later.</li> </ul> <h2>Bugfixes</h2> <ul> <li><code>MANIFEST.in</code> matching (via <code>FileList</code>) is now insensitive to Unicode normalization form. A pattern authored in one form (e.g. NFC, as typically saved by editors) now matches a file whose name is stored on disk in another (e.g. NFD, as produced by macOS APFS/HFS+). Previously an <code>exclude</code>, <code>global-exclude</code>, <code>recursive-exclude</code>, or <code>prune</code> rule could silently fail to drop a non-ASCII-named file from the source distribution, publishing it despite the exclusion -- see GHSA-h35f-9h28-mq5c.</li> </ul> <h2>Deprecations and Removals</h2> <ul> <li><code>pypa/distutils#334</code></li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/pypa/setuptools/commit/6519f728c6f23c992df81a5691ef7655184a20eb"><code>6519f72</code></a> Bump version: 82.0.1 → 83.0.0</li> <li><a href="https://github.com/pypa/setuptools/commit/d1151b125b29a6ee1c7db860f7ee6c365d525b5f"><code>d1151b1</code></a> Merge pull request <a href="https://redirect.github.com/pypa/setuptools/issues/5250">#5250</a> from pypa/feature/distutils-d7633fbed</li> <li><a href="https://github.com/pypa/setuptools/commit/a2df31e6a741b0fe775969618fe6a3a3d032e900"><code>a2df31e</code></a> Capture removal of dry_run parameter in changelog.</li> <li><a href="https://github.com/pypa/setuptools/commit/00144dc3fb6d023fd81cdc26c380a012b084df9b"><code>00144dc</code></a> Moved newsfragment to the release where it occurred.</li> <li><a href="https://github.com/pypa/setuptools/commit/a4a5a2b160357be77626aa077ddd1d8ee53be50f"><code>a4a5a2b</code></a> Add news fragment.</li> <li><a href="https://github.com/pypa/setuptools/commit/77470c23c35031c9e37d7512694ac5ca52bfcaac"><code>77470c2</code></a> Merge <a href="https://github.com/pypa/distutils">https://github.com/pypa/distutils</a> into feature/distutils-d7633fbed</li> <li><a href="https://github.com/pypa/setuptools/commit/3c43897420f25a1be1afe6eaa905d056009cc736"><code>3c43897</code></a> Merge pull request <a href="https://redirect.github.com/pypa/setuptools/issues/5247">#5247</a> from pypa/copilot/fix-pypy-version-issue</li> <li><a href="https://github.com/pypa/setuptools/commit/bb6ea66b4bd01cb35d7c68a1bc61b96d59190269"><code>bb6ea66</code></a> Bump PyPy from 3.10 to 3.11 in CI workflow</li> <li><a href="https://github.com/pypa/setuptools/commit/a2bc3aca2f55eb9b93ea633e58ba68170ce14451"><code>a2bc3ac</code></a> Fix broken intersphinx reference to build's installation docs</li> <li><a href="https://github.com/pypa/setuptools/commit/2d6a739c64cfedc65e1f635af7b52340aac8d99b"><code>2d6a739</code></a> Use stacked parametrize decorators instead of itertools.product</li> <li>Additional commits viewable in <a href="https://github.com/pypa/setuptools/compare/v82.0.1...v83.0.0">compare view</a></li> </ul> </details> <br /> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/a2aproject/a2a-python/network/alerts). </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Iva Sokolaj <102302011+sokoliva@users.noreply.github.com>
…ect#1160) ## Summary - Updates `SECURITY.md` to direct security reports to GitHub Security Advisories on this repository instead of `g.co/vulnz`. - Mirrors the updated policy in [`a2aproject/A2A`](https://github.com/a2aproject/A2A/blob/main/SECURITY.md), pointing to [Security Advisories · a2aproject/a2a-python](https://github.com/a2aproject/a2a-python/security/advisories).
# Description Update the A2A logo in README.md To preview changes go to [Link](https://github.com/a2aproject/a2a-python/blob/da02b3c690552094e04ac3d27f4ff8e557b4561c/README.md). - [X] Merge PR after approval Co-authored-by: Ivan Shymko <ishymko@google.com>
# Description Switches itk scripts to use new version of launcher Confirmation and testing done in fork: https://github.com/JakubWorek/a2a-python/actions/runs/31666233084
…1162) The in-memory server singletons guard their state with an asyncio.Lock, which binds to the first event loop that awaits it. A server that uses these singletons from more than one event loop then hits "RuntimeError: ... is bound to a different event loop" (or a cross-loop hang). Replace the asyncio.Lock with a loop-agnostic threading.RLock in InMemoryQueueManager, ActiveTaskRegistry, InMemoryTaskStore, InMemoryPushNotificationConfigStore, and the request handler's _running_agents guard, moving awaited calls outside the lock so each critical section is a pure in-memory dict operation. Behavior is unchanged for single-loop servers; only the private lock attribute's type changes. Tests: added tests/server/test_cross_event_loop_locks.py (cross-loop regression) and updated the test_init lock-type assertion. Co-authored-by: Ivan Shymko <ishymko@google.com>
🤖 I have created a release *beep* *boop* --- ## [1.1.3](a2aproject/a2a-python@v1.1.2...v1.1.3) (2026-08-18) ### Bug Fixes * use threading.RLock for in-memory server singletons ([a2aproject#1162](a2aproject#1162)) ([5bae35b](a2aproject@5bae35b)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
…patch (a2aproject#1137) Fixes a2aproject#1136 (the runtime issue split out of a2aproject#1101). ### Problem One undrained sink stalls `EventQueueSource._dispatch_loop` for every sink. The dispatcher's asyncio.gather awaits a blocking put on each sink, so a single full queue (default cap 1024) whose consumer went away blocks the gather forever. The incoming queue then fills, producers wedge in enqueue_event, and the task's event flow never recovers; see the task dumps and repro in a2aproject#1101. ### Change: split the sink semantics The two kinds of sink want different fullness behavior. The default sink is flow control. Its consumer drives task state, so a full queue that back-pressures the dispatcher (and, transitively, the producer) is correct, and it stays exactly as it is. A slow task store slows the pipeline down rather than losing events. Tapped subscriber sinks are broadcast observers. Their consumers are remote and can be abandoned, most simply by a non-blocking message/send whose HTTP response has already returned. So `tap()` gains `evict_on_full: bool = False`. When an evict-on-full sink is full at delivery time, the dispatcher force-closes it (close(immediate=True), warning logged) and it detaches through the existing remove_sink path; dispatch to the remaining sinks continues, and any producer parked on the incoming queue recovers. The consumer of an evicted sink sees `QueueShutDown` on its next dequeue, same as any closed queue. There is no new exception type and no API surface beyond the keyword. The test is fullness at delivery time and nothing else. An abandoned consumer is the case that motivated this, but a consumer that is alive and reading and has simply fallen `max_queue_size` events behind hits the same predicate and is evicted the same way. At the production default that is 1024 events behind, and at max_queue_size=2 a consumer sleeping five milliseconds between reads is evicted after draining one event. The warning text and the `_deliver_to_sink` docstring currently say the consumer "is not draining it" and call it an abandoned subscriber, which claims something the check never tested, and a reword is coming that names the queue bound instead and makes no claim about the consumer. A sink that already closed since the dispatch snapshot was taken is skipped before the fullness check, so a graceful close(immediate=False) whose consumer is still draining is never upgraded to immediate=True and keeps its trailing events. The full() pre-check is race-free: the dispatcher is the only writer to a sink queue and consumers only read, so a queue observed non-full cannot become full before the put. `tap()` defaults to False, so the documented blocking contract is unchanged for existing callers. ActiveTask's subscriber tap opts in explicitly, and that is the one production site that wedges, so the fix reaches existing deployments without changing the default of the public primitive. ### Tests Five tests. test_evict_on_full_sink_is_evicted_and_dispatch_continues puts a full capacity-1 evict sink through dispatch and asserts it is closed and detached while the default sink still receives every event. test_producer_unblocked_after_evict_on_full is the a2aproject#1101 runtime shape in miniature: producers blocked on a full incoming queue recover once the sink is evicted. test_default_tap_keeps_backpressure pins the existing flow-control contract, a default tap still blocking dispatch when full. test_graceful_close_not_upgraded_to_immediate_by_eviction covers the skip above, a gracefully closing full sink keeping its trailing events instead of being force-closed. test_subscriber_taps_are_evict_on_full asserts that ActiveTask.subscribe taps its subscriber sinks with the keyword set. The eviction and producer-unblock tests fail without the fix. Full suite: 1761 passed, 90 skipped, 3 xfailed, 1 xpassed. ### Out of scope The deeper root cause is subscriber-sink lifecycle: a sink tapped for a consumer that has gone away should be closed by its owner at connection teardown, not discovered full later. That needs its own look at the request-handler layer. This PR keeps the dispatch layer from letting any such sink take the task down in the meantime. Edited 2026-08-20 to say what the eviction check actually tests. The description read as though only an abandoned consumer is evicted; the predicate is queue fullness, so a live consumer more than `max_queue_size` events behind is evicted too, and the warning wording that claims otherwise is being reworded. --------- Co-authored-by: mykytanetipa <mykytanetipa@google.com>
…2aproject#1159, a2aproject#1170) (a2aproject#1172) ## Summary Two related fixes to `DefaultRequestHandlerV2`, shipped together because the first is a prerequisite for the second to be safe. **Fixes a2aproject#1159 (owner-scoping, the security base).** `on_cancel_task` and `on_subscribe_to_task` resolved a live task via `ActiveTaskRegistry.get_or_create(task_id)` by `task_id` alone, skipping the owner-aware `task_store.get(task_id, context)` that `on_get_task` and the send path already perform. A caller who knows another tenant's `task_id` could subscribe to its live stream and cancel it (CWE-639). Both handlers now consult the owner-aware store first and fail closed with `TaskNotFoundError` (masking existence, matching `on_get_task`). **Fixes a2aproject#1170 (cancel writes a terminal state).** `ActiveTask.cancel` cancelled the producer before `AgentExecutor.cancel`, so the component that owns the terminal state could not write it; and a task parked non-terminal (e.g. `input-required`) reported cancel-success without transitioning. The executor cancel now runs before the producer cancel (producer still cancelled on the error path), and cancellation that leaves the task non-terminal is closed out as `CANCELED`. ## Why one PR a2aproject#1170 alone makes a2aproject#1159 worse: for a parked task, a cross-tenant cancel via the a2aproject#1159 bypass changes from a harmless no-op into an actual cross-tenant `CANCELED` write. Landing a2aproject#1159's owner check together (as the base commit) removes that window. Merging them together, or a2aproject#1159 before a2aproject#1170, is safe; a2aproject#1170 must not land first. ## Tests - Owner-scope regressions: non-owner cancel and non-owner subscribe are rejected (`TaskNotFoundError`), including a parked task; the owner still succeeds. - Cancel-terminal regressions: mid-run cancel reaches a terminal state; cancel of a parked task does not silently succeed (the maintainer repros from a2aproject#1171, converted from xfail to passing). ## Validation - Owner-scope tests: reverting the a2aproject#1159 handler change fails all three; restore passes. Full request-handler module: 62 passed. - Cancel-terminal tests: reverting the a2aproject#1170 change fails both scenario_19 tests; restore passes. Full integration module: no regression. - Combined: with both applied, a cross-tenant cancel of a parked task is rejected rather than writing `CANCELED`. - `ruff` clean at the pinned version. --------- Signed-off-by: AlgoVoi <chopmob@gmail.com> Co-authored-by: Josh Nichols <joshua.nichols@gmail.com>
…lerV2 (a2aproject#1153) ## Summary Fixes a2aproject#1135. `DefaultRequestHandlerV2.__init__` accepts a `queue_manager` argument (kept for signature compatibility with `DefaultRequestHandler`) but never stores or uses it. As @rohityan confirmed on the issue, v2 delegates event streaming to an in-memory `ActiveTaskRegistry`, so any custom or distributed `QueueManager` — e.g. a Redis-backed one used for multi-replica stream reconnection — is silently dropped. After upgrading, multi-replica streaming breaks with no error or warning. ## Fix Emit a `logger.warning` at construction when a non-`None` `queue_manager` is passed, explaining that it is ignored in v2 and pointing at the documented workarounds (switch to `LegacyRequestHandler`, or route `/tasks/{id}:subscribe` to the replica holding the `ActiveTask`). This turns a silent misconfiguration into a visible one without changing v2's architecture. Also corrected the now-inaccurate inline comment on the parameter. ## Evidence - The parameter is dropped: `default_request_handler_v2.py` `__init__` binds `agent_executor`/`task_store`/etc. but has no `self._queue_manager = queue_manager` (contrast `default_request_handler.py:123`, which does store it and uses it at lines 209/313/503/611). - v2 uses the registry instead: `self._active_task_registry = ActiveTaskRegistry(...)`. ## Testing Added two tests to `tests/server/request_handlers/test_default_request_handler_v2.py`: - `test_init_warns_when_queue_manager_passed` — asserts a WARNING mentioning `queue_manager` is logged (fails on `main`, passes with the fix). - `test_init_no_warning_without_queue_manager` — asserts no such warning in the default case. ``` uv run pytest tests/server/request_handlers/ # 195 passed ``` `ruff check`, `ruff format --check`, and `ty check` all clean on the changed files. This change was developed with AI assistance; I verified the root cause and behavior against the code paths cited above, reviewed every line, and ran the tests. --------- Co-authored-by: mykytanetipa <mykytanetipa@google.com>
) ## Summary `_InMemoryTaskStoreImpl.save` and `InMemoryPushNotificationConfigStore.set_info` created owner buckets before acquiring their existing `RLock`. Two threads could both observe a missing owner, create separate buckets, and overwrite the bucket containing the first completed write. This moves owner-bucket creation inside the lock with `setdefault` in both stores. Threaded regression tests cover concurrent first writes and confirm that both records remain available through the public retrieval methods. ## Testing `uv run pytest tests/server/tasks/test_inmemory_task_store.py tests/server/tasks/test_inmemory_push_notifications.py -q` Result: 40 passed. Signed-off-by: Anas Khan <83116240+anxkhn@users.noreply.github.com> Co-authored-by: mykytanetipa <mykytanetipa@google.com>
Fixes a2aproject#1174. The `_canonicalize_agent_card` function builds the bytes that a signature covers, so whatever it produces is what a verifier in another SDK has to reproduce exactly. Today it ends in one call: ``` json.dumps(cleaned_dict, separators=(',', ':'), sort_keys=True) ``` That does not produce RFC 8785 on any of the three axes the scheme exists to fix, and I ran each one against the code as it stands before changing it rather than reasoning about it. A card named Café Agent serializes its name as `{"name":"Caf\u00e9 Agent"}`, where JCS section 3.2.2.2 asks for the literal UTF-8 bytes `{"name":"Café Agent"}` and escapes only the C0 range, the quote and the backslash. sort_keys=True orders keys by code point, where section 3.2.3 asks for UTF-16 code unit order, so a card whose extension params carry the keys U+1F600 and U+FF01 comes out in the opposite order from every conforming implementation. And repr formats numbers, where section 3.2.2.3 asks for the ECMAScript Number::toString algorithm, so an extension param of 0.000001 is written 1e-06 and an integral param of 1 is written 1.0. Every one of those four canonical forms disagrees with an independent implementation of the RFC; after the change all four agree byte for byte. There is a fourth divergence underneath the three in the issue, and it is the one that would have made a fix on the first three ineffective. The signer parsed the canonical string back into a dict and handed the dict to jwt.encode, which serializes its payload with its own json.dumps and therefore re-applies ensure_ascii=True to bytes that had just been canonicalized. The canonical form would have been correct and the bytes actually signed would still have been escaped. The signer now hands the canonical bytes to the JWS layer directly. The protected header is byte-identical either way, which the tests assert against a jwt.encode reference so that this cannot regress into a silent compatibility break in the header a verifier reads kid and alg out of. On the question you left open, of vendoring a package versus writing the algorithm here: I wrote it here, and I put the package in as a test-only oracle instead, because that is strictly stronger than either option on its own. The `rfc8785` package on PyPI is Trail of Bits' implementation, Apache-2.0, a pure-Python py3-none-any wheel with no runtime dependencies of its own and requires-python >=3.8, so it installs cleanly across the whole 3.10 to 3.14 matrix; its most recent release is 0.1.4 from 2024-09-27, which for a finished implementation of a frozen RFC is a reasonable place to sit rather than a warning sign. The reason not to make it a runtime dependency of `a2a-sdk[signing]` is that the signing extra is the code that decides whether a card is trustworthy, and a dependency there is one more package an attacker has to compromise to change what a signature means. The reason to use it anyway is that if it were the implementation, nothing would be checking it. As the oracle it checks this implementation on every accepted vector, alongside a corpus whose expected values two other implementations already agree on, so the change lands with three-way agreement instead of one-way trust. The expensive half is number formatting and I did not write that from scratch either: it is adapted from the reference implementation at `cyberphone/json-canonicalization`, Apache-2.0, credited in the module docstring, and then checked against the oracle over twenty thousand seeded random doubles and every mantissa-and-exponent pair near the notation thresholds. If you prefer that the SDK depend on rfc8785 at runtime, say so and I will make that swap; it is a one-line change to _canonicalize_agent_card and the entire test suite here stays valid, because the tests are written against the RFC rather than against this implementation. The tests are lifted rather than invented. The 57 vectors in `tests/utils/jcs_vectors.json` come verbatim from the language-neutral `a2a-jcs-v01` corpus proposed in a2aproject/a2a-tck#228, which is 47 accept, 10 reject, and five groups covering signature exclusion, key ordering, string serialization, number serialization, and arrays and nesting. Each vector carries its expected canonical form as a UTF-8 hex string, so a transcription mistake cannot hide inside an editor's encoding, and each expected value was produced by two independent implementations written by neither SDK author which agree byte for byte. I verified the lift programmatically rather than by eye: all 57 ids present, no extras, and zero mismatches on disposition, input and expected hex against the vector files in that PR. On top of the corpus the suite adds the cases this ecosystem has already found implementations disagreeing on: unpaired surrogates in keys and in values, noncharacters, characters above the BMP, U+2028 and U+2029, negative zero, denormals, the 1e21 and 1e-7 notation boundaries, integers past the exact-integer range, and nesting on both sides of the depth bound. That is 165 tests, and the full suite is 1950 passed with ruff check, ruff format --check and ty check all clean. Then I tried to break it, which is where the depth bound comes from. Nesting is attacker-controlled: `AgentExtension.params` is a `google.protobuf.Struct` and nests arbitrarily, and an unbounded recursive serializer turns a hostile card into a stack exhaustion in whoever verifies it rather than a rejected signature. Serialization stops at 128 levels, and the `_clean_empty` helper needed the same bound because it runs first and would otherwise be the crash site while the serializer's bound sat unreachable behind it. A card past the bound now fails as `InvalidSignaturesError` on the verify path rather than as a new exception type at callers. I also checked the mutations: reversing the key order to code point, restoring ensure_ascii, restoring repr for numbers, removing the depth cap, emitting -0, and removing the surrogate rejection each turn the suite red, at 7, 35, 19, 4, 4 and 6 failures respectively. These tests fail for the reasons they claim to exist. Two things worth knowing that are not this change. The compatibility break is real but narrow, and I measured its edges rather than describing them: a pure-ASCII card with no extension params canonicalizes identically before and after, and so does one whose extension params are ASCII strings, so those signatures keep verifying. A card with any non-ASCII text anywhere, or any numeric extension param, produces different bytes and its existing signatures will not verify, and that is the point: those are exactly the cards that do not verify in other implementations today. Separately, while attacking the signing path I found that _clean_empty lets a card carry content the signature does not cover. A card whose extension params include `"policy": ""` canonicalizes to the same bytes as a card with no policy key at all, so a signature made over the second verifies the first. That behaviour predates this change and fixing it would be a second and larger break, so I have deliberately left it alone here; I have a reproducer and I am happy to open it as its own issue if you want it tracked. --------- Signed-off-by: Sankalp Gilda <sankalp.gilda@gmail.com> Co-authored-by: mykytanetipa <mykytanetipa@google.com>
# Description regenerate itk uv lock to add dependency added in a2aproject#1193
… update dependency version markers (a2aproject#1203) # Description one lock file to avoid a2aproject#1202 problem in the future
# Description Thank you for opening a Pull Request! This pull request changed `EventQueueSource.remove_sink()` to use `set.discard()`, so removing an already-absent sink completes without raising `KeyError`. `EventQueueSink.close()` now delegates directly to that idempotent operation instead of suppressing the exception after telemetry has recorded it. A regression test closes the same sink twice and verifies that the mocked OpenTelemetry span records neither an exception nor an error description. Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Follow the [`CONTRIBUTING` Guide](https://github.com/a2aproject/a2a-python/blob/main/CONTRIBUTING.md). - [x] Make your Pull Request title in the <https://www.conventionalcommits.org/> 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 and linter pass (Run `bash scripts/format.sh` from the repository root to format) - [x] Appropriate docs were updated (if necessary) Fixes a2aproject#1133 🦕 --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: mykytanetipa <mykytanetipa@google.com> Copilot-Session: 42ca73c6-08fb-4954-96f4-b7e32e46613a
# Description Making use of shared scenarios and run_itk script from itk repository
…roject#1173) ## Summary The `on_create_task_push_notification_config` handlers (v1 and v2) stored client-supplied URLs without any validation. A caller who can create a push notification config can point the server at loopback, private-network, link-local, or cloud-metadata hosts, and the server will POST task events to that URL on every state change. ## Root cause `src/a2a/server/request_handlers/default_request_handler.py` and `default_request_handler_v2.py` call `push_config_store.set_info(task_id, params, context)` without checking `params.url`. The dispatch path (`BasePushNotificationSender._dispatch_notification`) is covered by a2aproject#1164; this PR closes the **write path**. ## Fix - Added `push_url_validation_error` to `base_push_notification_sender.py` (same logic as a2aproject#1164: blocks non-http(s) schemes, loopback, private, link-local, multicast, reserved, and unresolvable hosts). - Called it in both `on_create_task_push_notification_config` handlers before storing the config. - Raises `InvalidParamsError` with a descriptive message on rejection. ## Testing - `uv run pytest tests/server/request_handlers/ -k push_notification` — 55 passed. - New test: `test_on_create_task_push_notification_config_rejects_invalid_url` covers loopback and `file:` scheme rejection. - Updated existing tests that used unresolvable fixture URLs (`1.example.com`, `callback.com`) to use `example.com` (resolvable in CI). ## Relation to a2aproject#1164 a2aproject#1164 validates at dispatch time (read path). This PR validates at config creation time (write path). Both are needed: write-time validation fails fast and gives the client immediate feedback; dispatch-time validation is a defense-in-depth backstop. Made with [Cursor](https://cursor.com) --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Iva Sokolaj <102302011+sokoliva@users.noreply.github.com>
# Description The A2A specification requires each Task in a ListTasks response to omit the `artifacts` field when `includeArtifacts` is false or unset. The protobuf response already excludes artifact content, but JSON serialization with default fields enabled emitted `artifacts: []`. This change centralizes ListTasks wire serialization for REST and JSON-RPC. It preserves the existing pagination fields while removing the `artifacts` key unless the request explicitly enables it. When enabled, the field remains present with its actual content, including an empty array for tasks without artifacts. Regression tests cover both the default behavior and `includeArtifacts=true` across both protocol bindings. - [x] Follow the [`CONTRIBUTING` Guide](https://github.com/a2aproject/a2a-python/blob/main/CONTRIBUTING.md). - [x] Make the Pull Request title in the <https://www.conventionalcommits.org/> specification. - [x] Ensure the tests and linter pass (`bash scripts/format.sh` / project lint checks). - [x] Appropriate docs were updated (not required; this aligns serialization with the existing protocol contract). Validation: - Route suites: 40 passed - Full suite: 1974 passed, 90 skipped, 3 xfailed, 1 xpassed - Coverage suite: 93% total coverage (88% required) - `./scripts/lint.sh` (Ruff, format, and ty) Fixes a2aproject#1211 🦕 Co-authored-by: mykytanetipa <mykytanetipa@google.com>
…ject#1209) Follow-up to a2aproject#1137, documentation only. No behavior change: the diff touches two docstrings, one comment and one log format string. Ruff's linter and formatter are both clean on the two files, and the 73 tests in the event-queue and active-task modules pass. a2aproject#1137 added eviction so one sink whose queue has filled cannot wedge the dispatcher for every other subscriber. The behavior is right and the words around it are not. The guard is `if sink._evict_on_full and sink.queue.full():`, which tests queue fullness at delivery time and says nothing at all about the consumer. The docstring and the warning both described it as an abandoned subscriber whose consumer had stopped draining, which is a claim the check never makes. A consumer that is alive and reading steadily, merely `max_queue_size` events behind the dispatcher, satisfies the same predicate and is evicted the same way. That gap costs an operator real time. The warning as written sends someone looking for a dead consumer, and what they will find is a live one that is slow, with nothing in the message saying that is expected. So the warning now says the queue was full at delivery time and that the sink is therefore at least max_queue_size events behind, and it states outright that this does not mean the consumer stopped reading. Alongside it, the delivery docstring gains one paragraph saying what the test is and what it is not, the evict_on_full parameter doc names the queue bound rather than abandonment, and the comment at the subscriber tap site says the same thing. Abandonment stays where it is genuinely the subject: the teardown docstring still describes a producer wedged closing an abandoned sink, and the regression test still calls its never-drained sink abandoned, because in both of those cases that is what is happening. --------- Signed-off-by: Sankalp Gilda <sankalp.gilda@gmail.com> Co-authored-by: mykytanetipa <mykytanetipa@google.com>
…etime Introduces AgentInputQueue and changes AgentExecutor.execute() to accept it as a third parameter. All user messages — including the first — are now delivered via await input_queue.get(), which returns a RequestContext per turn. The context parameter retains call-level metadata (task_id, context_id, auth) but carries no message. ActiveTask._run_producer() calls execute() exactly once per task lifetime. Subsequent messages for the same task_id are enqueued to the running execute()'s input queue rather than spawning a second execute() call. The _request_lock is removed (serialisation was only needed for the former multi-execute design). LegacyRequestHandler (DefaultRequestHandler) gets a minimal single-item AgentInputQueue stub so agents using the new signature still work there without any multi-turn routing being added to the legacy path. All AgentExecutor implementations in tests and samples are updated to the new signature. Agents that previously returned after emitting INPUT_REQUIRED now loop on input_queue.get() and only return when the task reaches a terminal state, matching the single-execute contract. Signed-off-by: Sam Betts <1769706+Tehsmash@users.noreply.github.com>
Implements the SendLiveMessage operation from A2A spec PR #2153: bidirectional streaming where the client pushes a stream of StreamRequest messages (Message or TaskArtifactUpdateEvent) while receiving a stream of StreamResponse events. - Update buf.gen.yaml to proto commit from PR #2153 and regenerate types (StreamRequest, TaskMessageUpdateEvent, TimelineEntry, etc.) - Add on_live_message_send to RequestHandler ABC with UnsupportedOperationError default - Implement on_live_message_send in DefaultRequestHandlerV2: bootstrap task from first StreamRequest, feed follow-up messages via enqueue_request, subscribe without request so stream stays open until task finishes - Add SendLiveMessage bidi method to GrpcHandler - Add REST /live endpoint that returns UnsupportedOperationError (REST does not support bidi primitives) - Add send_live_message to ClientTransport base (UnsupportedOperationError default), GrpcTransport (full bidi), BaseClient, and Client ABC - Add integration tests for the gRPC bidi path Signed-off-by: Sam Betts <1769706+Tehsmash@users.noreply.github.com>
Client-supplied artifact_update payloads in SendLiveMessage were previously silently dropped. Now they are: 1. Persisted via the task event pipeline (enqueue_artifact_update injects the TaskArtifactUpdateEvent into _event_queue_agent, flowing through EventConsumer -> TaskManager.process() -> append_artifact_to_task()) so subscribers see the artifact update event automatically. 2. Delivered to the running agent executor via input_queue.get() as a RequestContext with artifact_update set, so the agent can react (inspect content, respond, etc.) without needing to re-persist. Adds artifact_update field to RequestContext and enqueue_artifact_update to ActiveTask. Integration test covers the full end-to-end path. Signed-off-by: Sam Betts <1769706+Tehsmash@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Description
Implements the
SendLiveMessagebidirectional streaming RPC from A2A spec PR #2153.SendLiveMessageallows a client to push a stream ofStreamRequestmessages (each containing aMessageorTaskArtifactUpdateEvent) while receiving a stream ofStreamResponseevents. This enables lower-latency real-time interaction and multi-turn in-flight agent conversations.Changes
buf.gen.yamlref to theherczyn:feat-bidicommit and regenerate types. New types includeStreamRequest,TaskMessageUpdateEvent,TimelineEntry; updatedStreamResponse,Task,Artifact,SendMessageConfiguration.RequestHandlerABC: Add non-abstracton_live_message_sendthat raisesUnsupportedOperationErrorso existing subclasses don't break.DefaultRequestHandlerV2: Full implementation — bootstrap the task from the firstStreamRequest, spawn a background feeder task to enqueue follow-up messages viaactive_task.enqueue_request(), and subscribe without a request so the stream stays open until the task finishes naturally.GrpcHandler: AddSendLiveMessagebidi method matching the generated servicer interface./liveendpoint that returnsUnsupportedOperationError— REST does not support bidi primitives./live POST(and/{tenant}/livevia the existing mount).ClientTransportbase raisesUnsupportedOperationErrorby default;GrpcTransportimplements full bidi;BaseClientandClientABC exposesend_live_message.artifact_updateStreamRequests: Client-supplied artifact updates are now fully handled. They are persisted viaActiveTask.enqueue_artifact_update()(injected into_event_queue_agent→EventConsumer→append_artifact_to_task(), identical path to agent-emitted artifacts) and also delivered to the running executor viainput_queue.get()as aRequestContextwithartifact_updateset, so the agent can react.RequestContextgains anartifact_updatefield for this purpose.tests/integration/test_live_message.pycovers the gRPC bidi path end-to-end: 3-message exchange, single-burst, empty stream error, and client artifact_update persisted and visible via GetTask.Note:
Task.timelinepopulation andtimeline_lengthquery wiring are deferred to a follow-up.CONTRIBUTINGGuide.bash scripts/format.shfrom the repository root to format)