ADR 0003 — Google ADK interoperability through official A2A¶
Status: Accepted
Date: 2026-08-28
Context¶
Tesserix ADK and Google Agent Development Kit are separate runtimes. Consumers need a standard boundary between them without importing Google objects into the Tesserix core or calling a second, Tesserix-specific peer protocol by the official A2A name.
The initial serving envelope is 20 task starts per second and 100 concurrent active tasks
per process. Input is at most 64 KiB of UTF-8 text and the final artifact is at most 1 MiB.
The adapter overhead objective is p99 below 10 ms at 100 concurrent tasks, excluding
model, network, gateway, and task-store latency. A task performs one request read and two
or three status/artifact writes. The library stores no durable data at 12 or 36 months;
the deployment-owned A2A TaskStore owns retained task volume. Five-times this envelope
requires a capacity test but not a protocol change.
The serving application has a 99.9% monthly availability objective. The bridge must fail closed and must not create a hidden fallback around its identity resolver, gateway, task store, or runner.
Prompts, tool authority, tenant data, model spend, and returned artifacts are the assets. An unauthenticated caller, an authenticated caller from another tenant, or a compromised dependency could target them. The trust boundary is the A2A server or gateway: it must authenticate the transport and place a verified principal in server context before an A2A message reaches the executor. Message metadata is never an identity source.
Decision¶
Tesserix ADK provides two optional public helpers:
a2a_agent_executorreturns an official A2A 1.xAgentExecutorbacked by oneAgentRunnerand reviewedAgentDefinition;google_adk_remote_agentcreates Google ADK 2.8's current, non-legacyRemoteA2aAgentfrom an official card or card URL.
The executor accepts user-role text parts only. It resolves a core Principal for both
execution and cancellation, binds tenant, subject, scopes, and principal context into the
run, and uses the official A2A task ID as the Tesserix run ID. An active task can only be
cancelled by the same tenant and subject after the resolver has independently authorized
the cancellation request. Rejection and failure messages are generic; internal exception
text is not returned to the peer.
Submitted, working, completed, failed, rejected, and cancelled states map onto official A2A task events. The authoritative final answer is buffered and emitted as one text or JSON artifact. Partial answer streaming, input-required/auth-required continuation, and push-notification delivery are not implemented by this bridge and must not be advertised unless the surrounding deployment supplies and tests them.
The official request handler and TaskStore remain the owners of persistence, duplicate
delivery, task lookup, subscriptions, and resubscription. InMemoryTaskStore is for local
development only. A persistent store preserves task state across a process restart, but
does not resume a model call that died with the process; production deployments need a
durable runner or reconciliation policy for tasks left working.
When identity resolution is unavailable or rejects a principal, execution is rejected and cancellation returns the non-enumerating official task-not-found error. When the runner or model fails, the task becomes failed with a stable public code. When the task store is unavailable, the official handler fails; there is no in-memory fallback that could lose tenant ownership or state. Duplicate sends and resubscription use the selected official store's semantics. Cancellation racing completion has one terminal-state winner.
The base installation remains unchanged. The a2a extra includes the official HTTP
server runtime. The google-adk admission adds one direct SDK and up to 20 transitive
packages only for consumers selecting that extra. Per active task, local bridge state is
one cancellation token and identity pair; input and output buffers remain within the
declared limits. Deployment cost is therefore dominated by model, network, and durable
task-store usage.
Alternatives¶
Writing a bespoke Google-to-Tesserix adapter was rejected because it would duplicate
official task, cancellation, card, and transport semantics and lock both runtimes
together. Treating Tesserix's typed peer protocol as official A2A was rejected because the
wire contracts differ. Putting an untyped gateway shim in front of AgentRunner was
rejected because tenant attribution, bounds, and terminal-state races would remain outside
the tested public surface. The simplest alternative—documenting manual official SDK
wiring—was rejected because every consumer would reimplement the same security boundary.
Consequences¶
Google ADK can consume a Tesserix agent while the Tesserix agent keeps any supported model provider, tool, store, or gateway composition. Other official A2A clients use the same endpoint, and custom registries or protocol bindings remain possible through the existing client factory and registry protocols.
The bridge is deliberately not a process host. Applications still mount official routes, authenticate requests, provide a tenant-scoped task store, apply rate and spend limits, and operate recovery. A2A SDK 1.1.2 emits a protobuf descriptor deprecation warning under protobuf 6; the end-to-end test names upstream a2aproject/a2a-python#1158 so a future SDK upgrade removes the narrow expectation rather than hiding all dependency warnings.
Rollback removes the mounted A2A routes and the two optional extras from the consuming application. No Tesserix run, state, tool, provider, or database schema migration is required. Existing Tesserix peer integrations and official A2A client/card helpers remain independent.