feat(spec-v1.0): support mixable catalogs and component-level catalogId resolution - #2079
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces support for mixing multiple catalogs within a single UI surface, updating the protocol documentation, JSON schemas, and test cases accordingly. Key changes include making catalogId optional on createSurface to act as a surface-level default, and adding an optional catalogId to components and function calls for explicit overrides. The review feedback highlights a critical JSON Schema validation bug where catalogId on FunctionCall will fail validation due to strict property evaluation rules, and suggests defining a FunctionCommon schema to resolve it. Additionally, the feedback recommends removing an outdated reference to clientCapabilities and clarifying that the 3-step resolution logic applies to both components and function calls.
…ommon inheritance
|
|
||
| - `surfaceId` (string, required): The unique identifier for the UI surface to be rendered. This must be globally unique for the renderer's lifetime. | ||
| - `catalogId` (string, required): A string that uniquely identifies the catalog (components and functions) used for this surface. Note that `catalogId` is a string identifier, not a resolvable URI; while it is conventionally formatted as a URI (e.g., `https://mycompany.com/1.0/somecatalog`) to avoid naming collisions across organizations, it does not need to point to any deployed resource or downloadable file. Renderer and agent developers must agree on shared catalogs with well-known IDs in order to build systems that are compatible with each other. | ||
| - `catalogId` (string, optional): A string that uniquely identifies the default catalog (components and functions) used for this surface. Note that `catalogId` is a string identifier, not a resolvable URI; while it is conventionally formatted as a URI (e.g., `https://mycompany.com/1.0/somecatalog`) to avoid naming collisions across organizations, it does not need to point to any deployed resource or downloadable file. Components and function calls on this surface that do not explicitly specify their own `catalogId` will use this surface-level default `catalogId`. |
There was a problem hiding this comment.
What about adding a note:
Catalog IDs defined at the component and function-call levels and surface-level catalogId must conform to a unified A2UI specification version.
There was a problem hiding this comment.
Added a note based on your suggestion, but with slightly different wording.
|
|
||
| This structure is designed to be both flexible and strictly validated. | ||
|
|
||
| #### Mixable catalogs and component resolution logic |
There was a problem hiding this comment.
How do we handle inlineCatalogs if specified?
There was a problem hiding this comment.
I updated the wording to specify that inline catalogs are also included in the catalogs that can be mixed.
…atalogs resolution in multi-catalog surfaces
| - Added `actionResponse` message structure (`ActionResponseMessage`) to allow the agent to respond to a specific action call using a unique `actionId` with a `value` or `error`. | ||
| - Added `callFunction` message structure (`CallFunctionMessage`) to support agent-initiated function execution. Removed `callableFrom` and `returnType` properties from the wire payload, relying on runtime catalog verification. | ||
| - Updated the `createSurface` message (`CreateSurfaceMessage`) to rename the `theme` field to `surfaceProperties`, and allowed passing initial `components` and `dataModel` directly inside the payload. | ||
| - Updated the `createSurface` message (`CreateSurfaceMessage`) to rename the `theme` field to `surfaceProperties`, allowed passing initial `components` and `dataModel` directly inside the payload, and made `catalogId` an optional parameter that acts as the surface's default catalog. |
There was a problem hiding this comment.
Nit: I know you didn't write this part, but Is it really a "rename"? I'd more think of it as removing theme and adding surfaceProperties: they're very different.
(Feel free to ignore, this is a tiny thing)
There was a problem hiding this comment.
Let me do this as a follow up.
| - Set the `version` field in all streamed JSON envelopes to `"v1.0"`. | ||
| - Change the MIME type of A2UI payloads in transport layers from `application/json+a2ui` to `application/a2ui+json`. | ||
| - Rename the `theme` field in `createSurface` messages to `surfaceProperties` and remove `primaryColor`. You can also pass initial `components` and `dataModel` directly in the `createSurface` payload. | ||
| - Rename the `theme` field in `createSurface` messages to `surfaceProperties` and remove `primaryColor`. You can pass initial `components` and `dataModel` directly in the `createSurface` payload, and `catalogId` is now optional (acting as the default catalog for that surface). |
There was a problem hiding this comment.
Nit: Yeah, could you clarify about theme being removed and surfaceProperties added here too?
(Feel free to ignore, this is a tiny thing)
There was a problem hiding this comment.
Let me do this as a follow up.
| "supportedCatalogIds": { | ||
| "type": "array", | ||
| "description": "An array of strings, where each string is an ID identifying a Catalog Definition Schema that the agent can generate. This is not necessarily a resolvable URI.", | ||
| "description": "An array of strings, where each string is an ID identifying a Catalog Definition Schema that the agent can generate. Multiple catalogs can be mixed in a single surface.", |
There was a problem hiding this comment.
Do we need the "resolvable URI" message still? It might matter if an agent gets the idea it can/must download it. I'd even go so far as to say "This is not a resolvable URI" or "This is only an identifying string" or some such.
There was a problem hiding this comment.
Agreed. I added back that sentence.
| ### `createSurface` | ||
|
|
||
| This message signals the renderer to create a new surface and begin rendering it. A surface must be created before any `updateComponents` or `updateDataModel` messages can be sent to it. While typically achieved by the agent sending a `createSurface` message, an agent may skip this if it knows the surface has already been created (e.g., by another agent). Once a surface is created, its `surfaceId` and `catalogId` are fixed; to reconfigure them, the surface must be deleted and recreated. | ||
| This message signals the renderer to create a new surface and begin rendering it. A surface must be created before any `updateComponents` or `updateDataModel` messages can be sent to it. While typically achieved by the agent sending a `createSurface` message, an agent may skip this if it knows the surface has already been created (e.g., by another agent). Once a surface is created, its `surfaceId` and default `catalogId` (if provided) are fixed; to reconfigure them, the surface must be deleted and recreated. |
There was a problem hiding this comment.
rm this line - for security an agent has to create a surface and then its the only agent that can interact with it. there is no pre created surfaces (eg, how would orchestrator routing work?)
While typically achieved by the agent sending a createSurface message, an agent may skip this if it knows the surface has already been created (e.g., by another agent)
There was a problem hiding this comment.
Well, this is true for the chat-based superapp use case where there are multiple agents which don't trust each other and therefore manage their own surfaces.
But there are other use cases where surfaces can be preexisting, especially non-chat canvas or dashboard use cases. The client may have a single surface with a hardcoded surfaceId that the agent continually updates. Or one agent may create a surface which another trusted agent then modifies.
I updated the wording based on your feedback to suggest that agents can only modify existing surfaces if they have access rights to them.
|
not for this PR, but we should start building a list of standard errors in client_to_server_schema |
Description of Changes
This PR updates the A2UI v1.0 specification, JSON schemas, and Python SDK to support mixing multiple catalogs within a single UI surface, based on the design consensus from #2020 and review feedback:
Mixable Catalogs & Inline Catalog Support:
renderer_capabilities.jsonandagent_capabilities.jsonthatsupportedCatalogIdscan be mixed within a single surface.supportedCatalogIdsand thecatalogIdof any inline catalog declared ininlineCatalogs(when supported by the agent).a2ui_protocol.mdandevolution_guide.mdrequiring that all catalogs mixed within a surface (at the component/function-call level or surface-default level) must conform to a unified A2UI specification version.Optional Component & Function
catalogId:catalogIdproperty (string) toComponentCommonandFunctionCommon(allOfpattern) incommon_types.json.catalog.jsoninheritFunctionCommonviaallOf, allowingcatalogIdoverrides on function calls while avoiding repetitive property definitions.IndexSystemFunction(@index) is defined as a spec system function and explicitly does not inheritFunctionCommonor permitcatalogId.rootOnlyaddition.Optional Surface Default
catalogId:catalogIdoptional inCreateSurfaceMessage(required: ["surfaceId"]), retaining it as an optional surface-level defaultcatalogIdfor components and functions that do not specify one.UpdateComponentsMessage,UpdateDataModelMessage, andDeleteSurfaceMessagedescriptions stating thatcreateSurfacemust have been previously sent for the surfaceId.Deterministic Component & Function Resolution Order:
a2ui_protocol.mdandevolution_guide.md:catalogId: Checked first if explicitly specified.catalogId: Checked second if defined increateSurface.clientCapabilitiesreferences torendererCapabilitiesina2ui_protocol.md.Python SDK Schema Helper Fix:
CatalogSchemaHelperinagent_sdks/python/a2ui_agent/src/a2ui/inference_formats/experimental/express/schema_helper.pyto crawlallOfsubschemas when looking up function parameters and argument definitions.Test Cases:
specification/v1_0/test/cases/initial_state_validation.json,call_function_message.json, andfunction_catalog_validation.jsonverifying valid component/function calls withcatalogIdand rejecting invalid@indexcalls withcatalogId.Rationale
Previously, each surface was strictly bound to a single catalog defined at surface creation time. In real-world composite application scenarios, surfaces often combine components or function calls from multiple catalogs (e.g. core UI components, domain-specific extensions, inline catalogs, and third-party tools). This change enables multi-catalog composition per surface while maintaining backward compatibility through surface default catalog fallbacks and deterministic resolution rules.
Testing / Running Instructions
Run v1.0 Schema Tests:
(Expected result: 138 passed, 0 failed)
Run Monorepo Validator:
(Expected result: Overall Validation: PASSED)