Skip to content

chore: Merge branch 'epic/1.0_breaking_changes' into 'main' - #589

Merged
ishymko merged 111 commits into
mainfrom
epic/1.0_breaking_changes
Jul 22, 2026
Merged

chore: Merge branch 'epic/1.0_breaking_changes' into 'main'#589
ishymko merged 111 commits into
mainfrom
epic/1.0_breaking_changes

Conversation

@JakubWorek

@JakubWorek JakubWorek commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Promotes 111 commits of A2A 1.0 work from epic/1.0_breaking_changes to main. Ships the v1.0 protocol implementation and an opt-in v0.3 backward-compatibility layer so v1.0 servers can transparently accept v0.3 clients (and vice versa).

bartek-gralewicz and others added 30 commits March 13, 2026 14:23
# Description

Updated the codebase to only use Proto based types and remove the old
JSON types.ts completely.

This change unifies the usage of types with other SDKs and is part of
the 1.0.0 scope. Equivalent PR on the Python SDK:
a2aproject/a2a-python#572.

# Important

This is a breaking change and therefore it is going to be merged into an
epic `v1.0` branch instead of `main`. Later 1.0 release will introduce
this in the `main` branch.

---------

Co-authored-by: Bartek Gralewicz <bgralewicz@google.com>
Co-authored-by: Ivan Shymko <ishymko@google.com>
# Description

Remove old JSON-RPC Client along with its usage and unit tests. Deleted
compatibility code that was present in `json_rpc_transport.ts`.

# List of changes:

- Removed `client.ts` and `client.spec.ts`.
- Moved exports of `A2AStreamEventData` and `SendMessageResult` to
`src/index.js`.
- Updated relevant imports.
- Updated `client_auth.spec.ts` to use transport logic directly.
- Removed no longer needed compatibility code from
`json_rpc_transport.ts`.

Fixes (partially) #179 🦕
# Description

Remove `ToProto`/`FromProto` usage where possible.

# Changes

- Removed unneeded `ToProto`/`FromProto` methods and their usage.
- Updated relevant unit tests.
- Expanded unit tests for `FromProto` module.

Fixes(partially) #179  🦕
# Description

Update to errors usage in `a2a-js`. Previously, errors were based around
JSON-RPC transport but since now, multiple transports are supported,
errors thrown by the server should be transport agnostic.

# List of changes

- Removed old `src/server/error.ts`.
- Replaced JSON-RPC errors with transport agnostic errors and proper
mapping when errors are thrown.
- Removed `as <Type>` usage in `grpc_transport.ts` as suggested here:
#356 (comment)

Fixes (partially) #179 🦕
# Description

Removal of `A2AExpressApp` in favor of using middleware directly.

# List of changes

- Removed `A2AExpressApp`.
- Updated tests for `A2AExpressApp` to use middleware directly.
- Setup logic that was previously present in the `A2AExpressApp` is now
present as a helper function in the test file.
  - Name update: `a2a_express_app.spec.ts` -> `express_app.spec.ts`.

Fixes (partially) #179  🦕
# Description

Applying the same code test coverage comparison changes as on the `main`
branch: #362 .
# Description

Introduced A2A 1.0.0 data model. New `a2a.ts` files are generates in
order to replace currently used `src/grpc/a2a_services.ts` and
`src/types/a2a_types.ts`. The codebase is not yet updated to use these
new types. The scope of this PR is to generate the new data model and
update yaml files and documentation that is used during generation.

# Flow

- Updated `src/types/buf.gen.yaml` and `src/grpc/buf.gen.yaml`.
- Ran `npx buf generate` from `src/types` and `src/grpc`.
- Manually updated types in `src/grpc/pb/a2a.ts` to use definitions
already present in `src/types/pb/a2a.ts` instead of doubling the
definitions.

Fixes #179  🦕
# Description

Drop support for node 18.

# List of changes

- CI workflows
  - Node setup actions update.
  - Node versions strategy matrices updated.
- `package.json` - update min supported version.
- `AGENTS.md` - tech stack update

Fixes (partially) #179  🦕
# Description

Update the codebase to use proto types generated for 1.0.0 spec version.

# List of changes

- Removed old type files: `a2a_services.ts` and `a2a_types.ts`.
- Update the codebase to use new types:
  -  `Message.content` -> `Message.parts`,
  - `TaskStatus.update` -> `TaskStatus.message`
- `SendMessageConfiguration.blocking` ->
`SendMessageConfiguration.returnImmediately` (reversed behavior),
- The AgentCard Structure updates: `supportedInterfaces` instead of
`url`, `preferredTransport`, and `additionalInterfaces`,
`securityRequirements` instead of `security`.
- `setTaskPushNotificationConfig` -> `createTaskPushNotificationConfig`
(+ arguments update).
- `listTasks` method is not implemented yet. If used, it will throw
`NotImplemented` error. There is a `TODO` next to it to mark the place
in code.

Fixes (partially) #179  #322 🦕
# Description

Remove obsolete JSON-RPC types.

# List of changes

- Remove temporary JSON-RPC types.
- Use Proto types payload directly.

Fixes #179 🦕
# Description

Exposing `protocolName` for all `Transport` layers in a unified way to
`TransportFactory`.

# List of changes

- Exposed `protocolName` for all transport types.
- Added new unit tests to check each transport for correctness of the
returned `protocolName`.
- BREAKING CHANGE: Removed static member of transport factories `name`
that held protocol type.

Fixes (partially) #179  🦕
# Description

Implementation of the `listTasks` method. Changes made accordingly to
the current spec definition as well as the following PRs:
- #173 
- a2aproject/a2a-python#511

Fixes #172  🦕
# Description

Align transport method names with 1.0 spec.

Aligning done based on the spec documentation:
https://a2a-protocol.org/latest/specification/#53-method-mapping-reference

Fixes:
- #326
- #327
- #328
# Description

Aligned error types with the protocol documentation:
https://a2a-protocol.org/latest/specification/#54-error-code-mappings

Fixes (partially) #321 🦕
# Description

Remove specific transport exports from the client's index file. We
should only export generic transport types and transport specific
factories.

Fixes (partially) #179 🦕
…405)

# Description

Made `ServerCallContext` a mandatory parameter across all extension
points.

Notes:

`InMemoryTaskStore` does not use the `ServerCallContext` but still
accepts it as an argument to align with `TaskStore` interface which it
implements.

Fixes #179  🦕
# Description

Add implementation of ListTasks method to the clients side. The server
side was already implemented before.

Fixes #221  🦕
…fig type (#410)

# Description

Remove `ToProto`/`FromProto` methods for
`listTaskPushNotificationConfig` type.

---------

Co-authored-by: Guglielmo Colombo <guglielmoc@google.com>
# Description

Enable ITK tests.

Currently, the testing is done between the current a2a-js state and Go
SDK 1.0 for the following configurations:
- Streaming
  - JSON-RPC / gRPC
  - HTTP_JSON
- Non-Streaming
  - JSON-RPC / gRPC
  - HTTP_JSON

# Notes

At the moment, there is an extensive middle layer in `itk_agent.ts`.
This middle layer is planned to be removed once ITK tests are less spec
dependent and a2a-js is closer to 1.0 release state.

Fixes (partially) #321 🦕
# Description

- Update the push notification methods to use `StreamResponse` instead
of relying only on `Task` object type.
- Replaced all `A2AStreamEventData` occurrences with `StreamResponse`.

By removing usage for `A2AStreamEventData` union type, dirty conversions
were possible to be removed for JSON-RPC transport.

Fixes #193  🦕
# Description

- Add `AgentEvent` wrapper type to remove duck-typing runtime checks for
event types.
- Simplification to:
  - `ToProto`/`FromProto` converters usage
  - `grpc_service`/`grpc_transport` wrapper functions and their usage.

Fixes #351  🦕

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
)

# Description

Sending a `Task` object as the first event after subscribing to it.

Additionally, throwing an `UnsupportedOperationError` error if a `Task`
in terminal state was used for subscribing. This behavior is aligned
with spec requirements:
https://a2a-protocol.org/latest/specification/#1046-subscribetotask

Fixes #323 🦕

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
# Description

There are currently 4 real TODOs in the codebase:

- Usage of `any` in `jsonrpc_transport_handler.ts` - **FIXED**
- Snake case and camel case acceptance for `historyLength` - **FIXED**
- Stale TODO in cli.ts mentioning exposing protocolName which was
already implemented - **TODO REMOVED**
- gRPC error mapping should be removed and the errors themselves
enriched - **NOT TACKLED, WILL BE DONE IN THE SEPARATE PR**.
# Description

Added support for multi-tenancy.

 ## Multi-Tenancy

This handler supports multi-tenant deployments through the `tenant`
field present
on all request objects (per A2A spec Sections 3.1.x and 4.4.6). The
tenant value
 flows through the system as follows:

1. **Transport layer** extracts tenant from the protocol-specific
source:
    - REST: URL path prefix (`/:tenant/...`)
    - JSON-RPC: `params.tenant` in the request body
    - gRPC: `tenant` field in the request message

2. **`ServerCallContext.tenant`** carries the tenant to all downstream
components,
    including `TaskStore`, `PushNotificationStore`, and `AgentExecutor`.

3. **`InMemoryTaskStore`** and **`InMemoryPushNotificationStore`** use
`context.tenant`
to scope data with composite keys (`{tenant}:{id}`), providing tenant
isolation.
 
Similar PR done in Python SDK:
a2aproject/a2a-python#758

Fixes #325 🦕
JakubWorek and others added 21 commits June 29, 2026 15:17
…ons (#555)

# Description

This pull request updates the project to the v1.0 beta release,
reflecting the new version across documentation and configuration files.
…d TCK compat agent workflow (#558)

# Description

Adds a CI workflow (run-tck-compat.yaml) that drives the upstream
a2a-tck@0.3.0.beta5 suite against a new SUT (tck/compat-agent/) — a
v1.0-native server with legacyCompat: { enabled: true } on every
transport. Triggers on main and epic/**, exercises JSON-RPC, REST, and
gRPC.

Fixes v0.3 compat-layer bugs the TCK surfaced:
- JSON-RPC error codes — parse errors now emit -32700 (was -32602);
bodies with missing/unknown method route through the v0.3 dispatcher and
surface -32600 per §3.6.2 default-to-v0.3 (new isV1JsonRpcMethod helper
drives the fallback).
- Translator input validation — toCoreMessage / toCorePart reject
malformed v0.3 messages with -32602 Invalid params instead of crashing
into -32603 Internal error.
- gRPC + REST error mapping — LegacyA2AError now translates to the right
grpc.status and HTTP 4xx via LEGACY_CODE_TO_GRPC_STATUS and a
compat-aware mapErrorToStatus (was falling through to UNKNOWN / 500).
- tasks/resubscribe always SSE — opens text/event-stream before peeking
the first event and emits errors as SSE events; strict v0.3 clients
reject anything else.
- REST output casing — v0.3 REST emits snake_case (context_id,
message_id, …) to match the v0.3 reference proto; input still accepts
both forms.
…it per-interface advertisement (#560)

# Description

v0.3 advertisement and request routing are now driven entirely by
per-interface protocolVersion declarations in the agent card. The SDK no
longer auto-promotes v1.0 interfaces to v0.3 when legacyCompat: {
enabled: true } is set. This aligns JS with the Python and Go SDKs and
fixes a issue where the card claimed v0.3 support for bindings (e.g.
gRPC) that the actual strict-v1.0 handlers would reject at the wire.

## Motivation
Before this change, legacyCompat: { enabled: true } on agentCardHandler
called toCompatAgentCard(card, { synthesize: true }), which restamped
every declared AgentInterface as v0.3-reachable regardless of its real
protocolVersion. Combined with validateVersion's implicit v0.3 add, the
SDK advertised and accepted v0.3 on bindings the operator never opted
into v0.3 for.

Card shape under the new model:
```ts
supportedInterfaces: [
  { url: '/jsonrpc', protocolBinding: 'JSONRPC',   protocolVersion: '1.0' },
  { url: '/jsonrpc', protocolBinding: 'JSONRPC',   protocolVersion: '0.3' },
  { url: '/rest',    protocolBinding: 'HTTP+JSON', protocolVersion: '1.0' },
  { url: '/grpc',    protocolBinding: 'GRPC',      protocolVersion: '1.0' },
]
```

→ v0.3 clients reach only JSON-RPC; REST and gRPC v0.3 calls return
VersionNotSupportedError. Symmetric across card-serving and routing.

Closes #559  🦕
…skip non complient tck test (#561)

# Description

Reverts snake_case transformation from rest handler and skips non
[spec](https://a2a-protocol.org/v1.0.0/specification/#343-multi-turn-conversation-patterns)
complient test.
🤖 I have created a release *beep* *boop*
---


##
[1.0.0-beta.0](v1.0.0-alpha.0...v1.0.0-beta.0)
(2026-07-01)

The v0.3 backward-compatibility layer is now complete, marking this
release as the first one bringing v1.0 and v0.3 support together.
Backward compatibility with v0.3 was implemented across all three
transports (JSON-RPC, REST, gRPC), agent-card resolution, and push
notifications — closing
[#452](#452).
A v1.0 server can transparently accept v0.3 clients (and a v1.0 client
can talk to v0.3 servers) by opting in with `legacyCompat: { enabled:
true }` on the relevant transport.

See the [v0.3 → v1.0 migration
guide](https://github.com/a2aproject/a2a-js/blob/v1.0.0-beta.0/docs/migration-guide.md)
and the [end-user v0.3 compatibility
guide](https://github.com/a2aproject/a2a-js/blob/v1.0.0-beta.0/docs/compatibility-v0_3.md).


### Features

* add compat-v1 client and server samples to demonstrate A2A v0.3
backward compatibility
([#520](#520))
([fce0b3f](fce0b3f))
* add opt-in legacy v0.3 compatibility to JSON-RPC handler and improve
extension header support
([#501](#501))
([f707350](f707350))
* add v0.3 compatibility layer for client and server agent-card handling
([#504](#504))
([75685f2](75685f2))
* add v0.3 gRPC compat layer
([#505](#505))
([314d9e3](314d9e3))
* default to v0.3 if version not provided
([#511](#511))
([ad7f772](ad7f772))
* defined compat-specific consts and mappings
([#478](#478))
([db0c595](db0c595))
* implement legacy v0.3 JSON-RPC transport handler
([#493](#493))
([d191d91](d191d91))
* implement legacy v0.3 REST support with compatible transport handler
and middleware routing
([#495](#495))
([da9a968](da9a968))
* implement v0.3 ITK cross-SDK validation workflows
([#513](#513))
([c9385f3](c9385f3))
* implement v0.3 JSON-RPC client transport
([#499](#499))
([46aa5d6](46aa5d6))
* implement v0.3 protocol compatibility for RestTransportFactory
([#503](#503))
([b172482](b172482))
* implement versioned push notification serializers with legacy v0.3
compatibility support
([#507](#507))
([0261be8](0261be8))
* normalize extension headers based on negotiated protocol version
([#510](#510))
([74a51ee](74a51ee))
* replace automatic v0.3 agent card synthesis with explicit
per-interface advertisement
([#560](#560))
([60ad3b2](60ad3b2))


### Bug Fixes

* **compat:** wrap primitive Part.data values instead of throwing
([#537](#537))
([7647db5](7647db5))
* **server/rest:** handle undefined historyLength to return full task
history ([#527](#527))
([fa9ebce](fa9ebce)),
closes [#535](#535)
* **server:** handle AUTH_REQUIRED lifecycle per spec
([#523](#523))
([68826c2](68826c2))
* **server:** make cancelTask idempotent on the canceled state
([#545](#545))
([1d17a78](1d17a78))
* **server:** make push notification config id optional with server-side
UUID ([#543](#543))
([83af8c3](83af8c3))
* **server:** merge Task history in ResultManager instead of overwriting
([#524](#524))
([4e1d4f7](4e1d4f7)),
closes [#532](#532)
* **server:** preserve activated extensions in response header
([#544](#544))
([26184b4](26184b4))
* **server:** propagate return to SSE generators to prevent listener
leaks ([#526](#526))
([a690734](a690734)),
closes [#534](#534)
* **server:** synthesize correct error Task id in blocking and streaming
paths ([#525](#525))
([a3dd703](a3dd703)),
closes [#533](#533)
* **server:** yield Task snapshot on resubscribe when bus is inactive
([#542](#542))
([cd8f8fc](cd8f8fc))

---
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: JakubWorek <worekkuba@gmail.com>
… RequestContext (#564)

Adds an optional readonly `metadata: Record<string, unknown>` property
to
`RequestContext`, populated from `SendMessageRequest.metadata` in both
`sendMessage` and `sendMessageStream`.

- The property is appended as a trailing optional constructor parameter,
so
  existing `RequestContext` construction sites are unaffected.
- It is `undefined` when the request carries no metadata — no behavior
change
  for existing executors.
- Two tests added to `default_request_handler.spec.ts`: request metadata
reaches the executor on both the unary and streaming send paths
(exercised
with the spec's own `a2a-service-parameters` pattern), and the property
  stays `undefined` when the request carries none.

# 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/google-a2a/a2a-js/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
- [x] Appropriate docs were updated (if necessary)

Fixes #563 🦕
## Summary

`SSE_HEADERS`, `formatSSEEvent`, `formatSSEErrorEvent`,
`parseSseStream`, and `SseEvent` were already defined, documented, and
tested in `sse_utils.ts` — but not re-exported from `src/index.ts`. This
forces anyone building a custom A2A adapter to inline these helpers
rather than import them.

This PR adds a single re-export block to `src/index.ts`. No logic
changes, no new tests needed.

## Changes

**`src/index.ts`**
```ts
export {
  SSE_HEADERS,
  formatSSEEvent,
  formatSSEErrorEvent,
  parseSseStream,
  type SseEvent,
} from './sse_utils.js';
```

## Testing

`npm run build` passes cleanly — ESM, CJS, and DTS all succeed.

## Related

Closes #547
# Description

This PR focuses on intorducing small compatibility fixes:
- ContentType: sending `application/json` which is only accepted by some
sdk instead of `application/a2a+json`
- adding `GET` support for `resubscibe` route
- sending file_with_bytes as raw bytes instead of encoding it with utf8

It also introduces new itk scenarios for added test agents.
…nt CLI (#574)

Lets the sample CLI talk to A2A endpoints that require Google
credentials but serve no agent card.

1. `--no-agent-card` synthesizes an AgentCard in-memory from the base
URL (positional arg) and connects via `createFromAgentCard` instead of
discovery.

1. `--google-auth` (also implied by `--agent-engine`) enables ADC
bearer-token auth. The card display and startup header now reflect the
synthesized card and active auth.

Tested:

```
$ npm run a2a:cli -- "<REDACTED>" --no-agent-card --google-auth

> a2a:cli
> tsx cli.ts <REDACTED> --no-agent-card --google-auth

A2A Terminal Client
Agent Base URL: <REDACTED>
Auth: Google ADC (Bearer token)
Using synthesized agent card (no discovery) for: <REDACTED>
✓ Using Synthesized Agent Card:
  Name:        External Agent
  Version:     1.0.0
  Streaming:   Supported
  Supported Transports: HTTP+JSON

✓ Connected via RestTransport
No active task or context initially. Use '/new' to start a fresh session or send a message.
Enter messages, or use '/new' to start a new session. '/exit' to quit.
External Agent > You: Hi from a2a-js, this is a test message, provide shortest reply possible
Sending message...

External Agent [11:11:09 AM]: ℹ️ Task Stream Event: ID: <REDACTED>, Context: , Status: TASK_STATE_SUBMITTED
   Task ID updated from N/A to <REDACTED>

. . . 
```
Add E2E test which spawns client and server samples and interacts with
the client CLI via stdio ([similar one in Python
SDK](https://github.com/a2aproject/a2a-python/blob/main/tests/integration/test_samples_smoke.py)).

Configured as a separate vitest config and a separate CI workflow to
keep unit tests fast.
# Description

This PR removes betterer in favour of enabling strict mode in tsconfig
for /src and base config based on previous one for other directories

Closes #182  🦕
…#364)

# Description

## Motivation

The [Python A2A SDK](https://github.com/a2aproject/a2a-python) exposes a
`state` property
on
[`ServerCallContext`](https://github.com/a2aproject/a2a-python/blob/main/src/a2a/server/context.py#L22)
— an arbitrary `MutableMapping[str, Any]` used to pass metadata through
the call pipeline.
The Python app layer actively uses it (e.g.
[`call_context.state['method'] =
method`](https://github.com/a2aproject/a2a-python/blob/main/src/a2a/server/apps/jsonrpc/jsonrpc_app.py#L149-L153)),
and op-SDK implementations rely on it to carry request-scoped data such
as tenant IDs,
auth tokens, or raw headers.

The TypeScript SDK was missing this capability, making it impossible to
faithfully port
Python-based agent implementations to TypeScript without architectural
workarounds.

## Changes

### `ServerCallContext`
- Added a `state` property — a `Map<string, unknown>` key/value bag,
directly mirroring
  Python's `state: MutableMapping[str, Any]`.
- Added `withRequestedExtensions()` method that returns a new context
with updated extensions
  while preserving `user`, `state`, and `activatedExtensions`.
- Extended the constructor to accept an optional `state` map.

### `ServerCallContextBuilder`
- Added `ServerCallContextBuilder` factory function type, mirroring
Python's abstract
  `CallContextBuilder.build(request)` pattern.
- Added `defaultServerCallContextBuilder` — the default implementation
that pre-populates
`state` with raw request headers under the `headers` key, mirroring
Python's
  `DefaultCallContextBuilder`.
- Added `STATE_HEADERS_KEY` constant for the headers key.
- Added `RequestHeaders` type — transport-agnostic representation of
request headers.

### Express handlers (`jsonRpcHandler`, `restHandler`)
- Added optional `contextBuilder?: ServerCallContextBuilder` to
`JsonRpcHandlerOptions`
and `RestHandlerOptions`, falling back to
`defaultServerCallContextBuilder`.
- Both handlers now pass raw `req.headers` into the context builder.

### gRPC handler
- Updated `grpc_service.ts` to pass request metadata as headers into the
context builder.

### Exports
- `ServerCallContextBuilder`, `RequestHeaders`,
`defaultServerCallContextBuilder`,
  and `STATE_HEADERS_KEY` are now exported from `@a2a-js/sdk/server`.

### Tests & samples
- Added unit tests for the new `ServerCallContext` API in
`test/server/context.spec.ts`.
- Added `src/samples/authentication/server_call_context.ts`
demonstrating custom context usage.

## Impact

This change enables direct migration of Python A2A agent implementations
to TypeScript —
any code relying on `context.state` in the Python SDK will have a direct
equivalent in the
TypeScript SDK with the same semantics.

## Checklist
- [x] Follow the [`CONTRIBUTING`
Guide](https://github.com/google-a2a/a2a-js/blob/main/CONTRIBUTING.md).
- [x] Make your Pull Request title in the
https://www.conventionalcommits.org/ specification.
- [x] Ensure the tests and linter pass
- [x] Appropriate docs were updated (if necessary)
…errors (#579)

# Description

`_processEvents` catches errors from the event-processing loop and
delegates to
`_handleProcessingError`, which — per its own comment — re-throws on the
blocking
path (no `firstResultRejector`) *"so the caller's await catches it"*:

```ts
} catch (error) {
  console.error(`Event processing loop failed for task ${taskId}:`, error);
  this._handleProcessingError(          // <-- not awaited
    error, resultManager, firstResultSent, taskId, options?.firstResultRejector,
  );
} finally {
  eventQueue.stop();
}
```

```ts
// Blocking: re-throw so the caller's await catches it.
if (!firstResultRejector) {
  throw error;
}
```

The call is **not awaited**. `_handleProcessingError` is `async`, so its
`throw`
becomes a floating rejection instead of propagating out of
`_processEvents`. The
generator resolves as if the drain had succeeded.

The one blocking caller that passes no `firstResultRejector` is
`cancelTask`'s
drain:

```ts
await this._processEvents(
  taskId, new ResultManager(this.taskStore, context), eventQueue, context,
);
```

So when the drain throws (e.g. the task store fails while persisting the
cancellation), `cancelTask` silently swallows the real error, reloads
the task,
sees a non-canceled state, and throws a misleading
`TaskNotCancelableError` —
while the true error surfaces as an unhandled promise rejection.

## Fix

Await `_handleProcessingError`. The blocking re-throw now propagates
through the
`finally` (which still detaches the queue) and out to the awaiting
caller,
matching the documented intent.

The non-blocking paths are unaffected: `sendMessage`/`sendMessageStream`
pass a
`firstResultRejector`, so `_handleProcessingError` rejects or persists a
FAILED
status rather than throwing.

## Tests

Added a regression test asserting `cancelTask` surfaces a drain-time
persistence
error instead of masking it with `TaskNotCancelableError`. Verified it
fails
without the fix (throws `TaskNotCancelableError`) and passes with it.

Full suite green (1368 tests).

- [x] Follows the `CONTRIBUTING` guide
- [x] PR title uses Conventional Commits (`fix:`)
- [x] Tests and linter pass
- [ ] Docs updated (not necessary)
…ctions (#580)

# Description

`readFrom` — used by `parseSseStream` for both the JSON-RPC and REST
client
transports — only calls `reader.releaseLock()` in its `finally`:

```ts
async function* readFrom(stream: ReadableStream<string>) {
  const reader = stream.getReader();
  try {
    while (true) {
      const { done, value } = await reader.read();
      if (done) break;
      yield value;
    }
  } finally {
    reader.releaseLock();   // detaches the reader, but never cancels the stream
  }
}
```

When a consumer stops iterating early — a `break`, a `throw`, or the
REST
transport throwing on an `error` event — the async generator's
`return()`/`throw()` runs that `finally`. `releaseLock()` detaches the
reader
but does **not** cancel the underlying `ReadableStream`, so the fetch
body (and
its socket) is left open. Repeated early terminations leak connections.

## Fix

Cancel the reader on teardown so cancellation propagates to the response
body:

```ts
} finally {
  await reader.cancel().catch(() => {});
  reader.releaseLock();
}
```

Semantics (verified against the WHATWG Streams behaviour):

- **early break / throw** → `cancel()` propagates to the source, closing
the
  connection (the fix).
- **normal completion** → the stream is already closed; `cancel()` is a
no-op.
- **errored source** → `cancel()` rejects with the stored error; it is
ignored
  so the original error still surfaces to the caller.

## Tests

Adds regression tests asserting the underlying stream is canceled when
the
consumer breaks early and when it throws mid-iteration. Both fail
without the
fix (the source `cancel` callback is never invoked) and pass with it.

Full suite green (1370 tests).

- [x] Follows the `CONTRIBUTING` guide
- [x] PR title uses Conventional Commits (`fix:`)
- [x] Tests and linter pass
- [ ] Docs updated (not necessary)
…stion (#582)

# Description

`parseSseStream` (shared by the JSON-RPC and REST client transports)
accumulates two buffers with **no size bound**:

- the line `buffer` — `buffer += value` on every chunk
- an event's joined `eventData` — consecutive `data:` lines are appended

Because A2A clients stream SSE from remote, potentially untrusted agent
servers, a malicious or broken server can exploit this as a
**denial-of-service
against the client**:

- stream bytes that **never contain a `\n`** → the line `buffer` grows
without bound
- stream endless `data:` lines with **no terminating blank line** →
`eventData` grows without bound

Either way the client keeps allocating without bound. Today that can
result in
a process-level OOM crash that the caller cannot handle as a normal
stream
error.

## Fix

Cap both accumulation points at `maxEventSizeBytes` and throw when
exceeded.
The throw runs the async generator's teardown, whose `finally` now
cancels the
reader (#580), so the offending connection is also closed. Net effect: a
**process-level crash becomes a catchable `Error`** that flows into the
transports' existing SSE-parse error handling.

The same limit applies to the unterminated line buffer because a single
SSE
field line is necessarily part of an event and should not be allowed to
exceed
the maximum event budget on its own.

- Default is **4 MiB** (matching gRPC's default max message size):
realistic
  A2A events (Message/Task JSON) are KB-scale, and large files should be
  referenced via `FileWithUri` parts rather than inlined.
- It is **configurable** via the new optional `maxEventSizeBytes`
argument for
callers that must inline larger payloads, and the error message points
at
  both options.

## Open question — is this the right layer?

I'm **not sure `parseSseStream` is the right place** for this, and would
appreciate maintainer guidance. Alternatives worth considering:

- enforcing it at the transport layer instead of the shared parser,
- deriving/validating against `Content-Length` where present,
- or leaving DoS mitigation entirely to the runtime / a reverse proxy.

Happy to change the default, make it opt-in, or move it elsewhere based
on
what you prefer.

## Tests

Adds regression tests for the unterminated-line,
terminated-oversized-line
(single-chunk), and unterminated-event cases — all would grow unbounded
without the cap — plus a within-limit sanity check.
`test/sse_utils.spec.ts`
is green.

- [x] Follows the `CONTRIBUTING` guide
- [x] PR title uses Conventional Commits (`fix:`)
- [x] Tests and linter pass
- [ ] Docs updated (not necessary)
…in RequestContext (#581)

# Description

In order to simplify the interface of `RequestContext`, provide more
data from the request and mimic [Python
sdk](https://github.com/a2aproject/a2a-python/blob/0d82ab985ffbac381a38adb5bd160674fa05eb18/src/a2a/server/agent_execution/context.py#L32)
it adds `request: SendMessageRequest` as a member of
`RequestContextParams`.
## Summary

- update the ts-proto generator from v2.6.1 to v2.11.0 so generated
`google.protobuf.Struct` wrappers preserve JavaScript `null`
- encode JavaScript `null` as `google.protobuf.NullValue.NULL_VALUE`
- decode the protobuf `nullValue` case back to JavaScript `null`
- add a wire-level regression test covering direct, nested, and array
metadata values

## Root cause

The generated `Value.wrap()` implementation assigned the literal
JavaScript `null` to a protobuf enum field. `@bufbuild/protobuf`
requires an integer enum value, so gRPC serialization failed with
`invalid int32: object`.

The reverse conversion also returned the enum value `0` instead of
restoring JSON `null`. The newer ts-proto output corrects both
directions so `google.protobuf.Struct` fields preserve JSON semantics
across the gRPC wire.

## Impact

Tasks, messages, and other A2A gRPC payloads can now contain valid JSON
`null` values in metadata or other `Struct`-backed fields without
crashing or changing them to `0` after deserialization.

## Target branch

This PR is rebased onto and directly targets `epic/1.0_breaking_changes`
for the stable 1.0.0 SDK work.

## Generation note

The checked-in 1.0 bindings contain post-generation adjustments. A full
`buf generate` currently rewrites those bindings and adds unrelated
generated files, so this PR refreshes only the affected `Struct`
artifact together with the generator pin to keep the fix scoped.

## Validation

- `npm test` — 66 files, 1,375 tests passed
- `npm run test:edge` — 49 files, 1,081 tests passed, 2 skipped
- `npm run build` — passed
- `npm run test-build` — passed
- `npm run lint:ci` — passed in GitHub Actions

Fixes #576
)

# Description

**Error Handling Refactor and Unification**
* All error classes now form a shared, transport-agnostic hierarchy with
A2AError as the base and semantic subclasses (e.g., TaskNotFoundError,
RequestMalformedError). Per-transport variants (e.g.,
RestTaskNotFoundError, GrpcTaskNotFoundError, JsonRpcTaskNotFoundError)
extend their semantic parent and carry transport-native context (HTTP
status/headers/cause, gRPC status/status-details-bin, JSON-RPC envelope
code/data). Type guards isRestError / isGrpcError / isJsonRpcError
narrow at catch time so callers keep instanceof TaskNotFoundError and
simultaneously get typed access to transport fields. Closes #317.
* Errors live at two dedicated subpaths: @a2a-js/sdk/errors (pb-free,
Workers-safe — base + semantic + REST + JSON-RPC) and
@a2a-js/sdk/errors/grpc (requires @bufbuild/protobuf for
grpc-status-details-bin encode/decode). The SDK root and /client /
/server no longer re-export errors, so non-gRPC consumers don't pull in
the pb peer dep.
* All transport implementations (rest, grpc, json-rpc, and legacy
variants) now use centralized error mapping functions from the new
modules (fromRestErrorBody, fromGrpcError, fromJsonRpcErrorResponse,
toRestErrorBody, toJsonRpcError, buildGrpcErrorMetadata, restStatusFor,
grpcStatusFor) instead of custom or scattered logic. Adding a new error
is one row in A2A_ERROR_SPECS — all wire mappings derive from it.
* The v0.3 compat layer replaces LegacyA2AError with a thin facade over
the new hierarchy that keeps the classic A2AError.taskNotFound(id) / new
A2AError(code, msg, data?) API. Wire codes without a v1.0 semantic twin
(PARSE_ERROR, INVALID_REQUEST, METHOD_NOT_FOUND) are preserved via
JsonRpc*Error.envelopeCode, so v0.3 clients keep seeing the same numeric
codes on the wire.

**Transport Implementation Simplification**
* Removed old error mapping methods from RestTransport, GrpcTransport,
JsonRpcTransportHandler, and their v0.3 compat counterparts in favor of
the centralized helpers, reducing code duplication and eliminating 6+
parallel error.name-keyed lookup tables.
* Added helpers to collect HTTP headers and provide richer error context
in REST transport errors.
* Deleted src/errors.ts (354 lines) and src/server/grpc/error_details.ts
(67 lines); folded into the new src/errors/ module.

**Build-tests Fix**
* The existing test-build script (esbuild --platform=neutral) never
actually enforced Workers-safe boundaries — platform=neutral bundles
Node-only modules silently, and CI's npm ci installs devDependencies
(which include @grpc/grpc-js and @bufbuild/protobuf), so the check was a
no-op. Added scripts/checkWorkersSafeBundles.js that fails if any
Workers-safe bundle inlines @grpc/grpc-js or @bufbuild/protobuf.
TDD-verified (goes RED when errors/grpc is re-exported from the pb-free
barrel, GREEN with the split).

**Documentation and Migration Guide Updates**
* Updated the migration guide to explain the new error class structure,
the two subpath entrypoints (@a2a-js/sdk/errors,
@a2a-js/sdk/errors/grpc), and the transport-specific catch-site pattern
with type guards, with code examples for the new patterns.


Closes #583 #317  🦕
Sync main (0.3.14) into the epic. Conflict resolutions:
- Kept epic's CHANGELOG, README, package.json version (1.0.0-beta.0),
  rest_handler.ts (GET+POST resubscribe already implemented as superset
  of main's #569), and its test coverage.
- Discarded main's docs edits and 0.3.14 CHANGELOG entry (release-please
  will regenerate the next entry from commit history).
- Preserved dependabot bumps on samples (@grpc/grpc-js 1.14.3->1.14.4,
  tsx 4.19.3->4.22.4).
- Bumped root @grpc/grpc-js devDep to ^1.14.4 to keep root and samples
  workspaces on the same version and avoid TS type-identity lint errors.
- npm-publish.yml, release-please.yml, and other workflows auto-merged
  cleanly (no divergence in file content).
@JakubWorek
JakubWorek requested a review from a team as a code owner July 22, 2026 10:40
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown

🧪 Code Coverage

⬇️ Download Full Report

Base PR Delta
src/client/card-resolver.ts 96.82% 84.28% 🔴 -12.54%
src/client/factory.ts 95.55% 95.62% 🟢 +0.07%
src/client/multitransport-client.ts 97.66% 97% 🔴 -0.66%
src/client/transports/grpc/grpc_transport.ts 93.92% 93.18% 🔴 -0.74%
src/client/transports/json_rpc_transport.ts 62.57% 69.78% 🟢 +7.21%
src/client/transports/rest_transport.ts 85.8% 91.92% 🟢 +6.12%
src/server/agent_execution/request_context.ts 100% 93.1% 🔴 -6.90%
src/server/events/execution_event_bus.ts 92.66% 92.12% 🔴 -0.54%
src/server/events/execution_event_queue.ts 94% 94.44% 🟢 +0.44%
src/server/express/agent_card_handler.ts 94.44% 100% 🟢 +5.56%
src/server/express/json_rpc_handler.ts 90.62% 78.53% 🔴 -12.09%
src/server/express/rest_handler.ts 85.55% 87.54% 🟢 +1.99%
src/server/grpc/grpc_service.ts 68.78% 80.82% 🟢 +12.04%
src/server/push_notification/default_push_notification_sender.ts 100% 96.59% 🔴 -3.41%
src/server/push_notification/push_notification_store.ts 89.18% 90.16% 🟢 +0.98%
src/server/request_handler/default_request_handler.ts 79.69% 87.83% 🟢 +8.14%
src/server/result_manager.ts 61.7% 82.19% 🟢 +20.49%
src/server/store.ts 100% 65.93% 🔴 -34.07%
src/server/transports/jsonrpc/jsonrpc_transport_handler.ts 76.68% 80% 🟢 +3.32%
src/server/transports/rest/rest_transport_handler.ts 98.78% 98.67% 🔴 -0.11%
src/server/utils.ts 0% 87.3% 🟢 +87.30%
src/sse_utils.ts 93.22% 95.12% 🟢 +1.90%
src/types/converters/from_proto.ts 74.88% 100% 🟢 +25.12%
src/types/converters/to_proto.ts 69.38% 95.23% 🟢 +25.85%
src/client/transports/pick_interface.ts (new) 100%
src/client/transports/tenant_transport_decorator.ts (new) 100%
src/compat/v0_3/client/card-resolver.ts (new) 100%
src/compat/v0_3/client/index.ts (new) 100%
src/compat/v0_3/client/transports/grpc/grpc_transport.ts (new) 91.13%
src/compat/v0_3/client/transports/grpc/index.ts (new) 100%
src/compat/v0_3/client/transports/json_rpc_transport.ts (new) 72.6%
src/compat/v0_3/client/transports/rest_transport.ts (new) 86.62%
src/compat/v0_3/constants.ts (new) 100%
src/compat/v0_3/index.ts (new) 100%
src/compat/v0_3/server/error.ts (new) 80.51%
src/compat/v0_3/server/express/agent_card_handler.ts (new) 93.05%
src/compat/v0_3/server/express/index.ts (new) 100%
src/compat/v0_3/server/express/rest_handler.ts (new) 71.73%
src/compat/v0_3/server/grpc/common.ts (new) 0%
src/compat/v0_3/server/grpc/grpc_service.ts (new) 95.05%
src/compat/v0_3/server/grpc/index.ts (new) 0%
src/compat/v0_3/server/index.ts (new) 100%
src/compat/v0_3/server/push_notification/index.ts (new) 100%
src/compat/v0_3/server/push_notification/v03_push_notification_serializer.ts (new) 91.17%
src/compat/v0_3/server/transports/jsonrpc/jsonrpc_transport_handler.ts (new) 100%
src/compat/v0_3/server/transports/rest/rest_transport_handler.ts (new) 99.01%
src/compat/v0_3/translate/_clone.ts (new) 100%
src/compat/v0_3/translate/agent_card.ts (new) 91.66%
src/compat/v0_3/translate/artifacts.ts (new) 100%
src/compat/v0_3/translate/enums.ts (new) 93.54%
src/compat/v0_3/translate/errors.ts (new) 100%
src/compat/v0_3/translate/index.ts (new) 100%
src/compat/v0_3/translate/messages.ts (new) 100%
src/compat/v0_3/translate/parts.ts (new) 98.42%
src/compat/v0_3/translate/push_notifications.ts (new) 100%
src/compat/v0_3/translate/requests.ts (new) 94.72%
src/compat/v0_3/translate/security.ts (new) 98.73%
src/compat/v0_3/translate/tasks.ts (new) 99.1%
src/compat/v0_3/translate/versions.ts (new) 100%
src/compat/v0_3/types/converters/from_proto.ts (new) 95.94%
src/compat/v0_3/types/converters/id_decoding.ts (new) 84%
src/compat/v0_3/types/converters/to_proto.ts (new) 99.6%
src/errors/base.ts (new) 97.79%
src/errors/grpc/grpc.ts (new) 99.41%
src/errors/grpc/index.ts (new) 100%
src/errors/index.ts (new) 100%
src/errors/json_rpc.ts (new) 100%
src/errors/rest.ts (new) 99.34%
src/server/owner_resolver.ts (new) 100%
src/server/push_notification/push_notification_serializer.ts (new) 100%
src/server/version.ts (new) 100%
src/signature.ts (new) 94.11%
src/version_utils.ts (new) 100%
Total 81.11% 90.89% 🟢 +9.78%

Generated by coverage-comment.yml

@JakubWorek JakubWorek changed the title feat!: promote A2A 1.0 (spec 1.0 + v0.3 compat layer) chore: Merge branch 'epic/1.0_breaking_changes' into 'main' Jul 22, 2026
@ishymko ishymko closed this Jul 22, 2026
@ishymko ishymko reopened this Jul 22, 2026
@ishymko
ishymko merged commit 0bb2fee into main Jul 22, 2026
54 of 55 checks passed
@ishymko
ishymko deleted the epic/1.0_breaking_changes branch July 22, 2026 11:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants