chore: Merge branch 'epic/1.0_breaking_changes' into 'main' - #589
Merged
Conversation
# 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 🦕
# 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 🦕
…ons (#555) # Description This pull request updates the project to the v1.0 beta release, reflecting the new version across documentation and configuration files.
Release-As: 1.0.0-beta.1
…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.
Release-As: 1.0.0-beta.0
🤖 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).
Contributor
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
ishymko
approved these changes
Jul 22, 2026
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.
Promotes 111 commits of A2A 1.0 work from
epic/1.0_breaking_changestomain. 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).