Skip to content

fix(v0_9): resolve v0.9.1 payload compatibility gap (Issue #1749)#1973

Open
Varun-S10 wants to merge 2 commits into
a2ui-project:mainfrom
Varun-S10:fix/issue-1749-v0.9.1-support
Open

fix(v0_9): resolve v0.9.1 payload compatibility gap (Issue #1749)#1973
Varun-S10 wants to merge 2 commits into
a2ui-project:mainfrom
Varun-S10:fix/issue-1749-v0.9.1-support

Conversation

@Varun-S10

Copy link
Copy Markdown
Collaborator

Description

This PR adds support for v0.9.1 protocol payloads in the v0_9 runtime across web_core, react, lit, and angular. Previously, the runtime rejected v0.9.1 messages because of hard-coded version strings and missing catalog aliases.

What changed:

  • Schemas & Types: Updated Zod validation and TypeScript types from "v0.9" to "v0.9" | "v0.9.1".
  • Catalog Resolution: Added an alias check in MessageProcessor so surfaces referencing the v0.9.1 basic catalog URL resolve correctly.
  • Package Exports: Added ./v0_9_1 package subpath exports to web_core, react, and lit.

This change is 100% backward compatible and does not affect any existing v0.9 features.

Fixes #1749

Pre-launch Checklist

One time:

For this PR:

  • I have added updates to the CHANGELOG.
  • I updated/added relevant documentation.
  • My code changes (if any) have tests.
  • If my branch is on fork, I have verified that scripts/e2e_test.sh passes.

If you need help, consider asking for advice on the discussion board.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds support for the v0.9.1 specification across the Angular, Lit, React, and Web Core renderers, including basic catalog alias resolution, Zod schema updates to accept both 'v0.9' and 'v0.9.1' versions, and package subpath exports. The feedback suggests improving the protocol version negotiation in MessageProcessor by advertising support for 'v0.9.1' in getClientCapabilities and dynamically returning the negotiated version in getClientDataModel instead of hardcoding 'v0.9'.

Comment on lines 74 to 80
const capabilities: A2uiClientCapabilities = {
'v0.9': {
supportedCatalogIds: this.catalogs.map(c => c.id),
supportedCatalogIds: this.catalogs.flatMap(c =>
c.aliases ? [c.id, ...c.aliases] : [c.id],
),
},
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To fully support v0.9.1 clients and servers, the getClientCapabilities method should also advertise support for the 'v0.9.1' protocol version in the returned capabilities object, rather than only advertising 'v0.9'. This ensures that v0.9.1 servers can correctly detect that the client supports this version during the capabilities exchange.

Suggested change
const capabilities: A2uiClientCapabilities = {
'v0.9': {
supportedCatalogIds: this.catalogs.map(c => c.id),
supportedCatalogIds: this.catalogs.flatMap(c =>
c.aliases ? [c.id, ...c.aliases] : [c.id],
),
},
};
const capabilities: Record<string, any> = {
'v0.9': {
supportedCatalogIds: this.catalogs.flatMap(c =>
c.aliases ? [c.id, ...c.aliases] : [c.id],
),
},
};
capabilities['v0.9.1'] = capabilities['v0.9'];

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this would be needed.

export const A2uiClientDataModelSchema = z
.object({
version: z.literal('v0.9'),
version: z.enum(['v0.9', 'v0.9.1']),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Since A2uiClientDataModelSchema now allows both 'v0.9' and 'v0.9.1' versions, we should ensure that getClientDataModel() in MessageProcessor returns the correct negotiated version dynamically instead of hardcoding 'v0.9'. This prevents potential validation failures on the server side when interacting with a v0.9.1 server.

You can implement this by tracking the version of the last processed message in MessageProcessor (e.g., via a private property updated in processMessage) and returning that version in getClientDataModel().

/**
* An optional array of alias URLs for the catalog.
*/
aliases?: readonly string[];

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't correct, since the v0_9_1 catalog and the v0_9 catalog are different and aren't interchangable. They aren't aliases for each other, and need to be treated separately.

id?: string;

/**
* An optional array of alias URLs for the catalog.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They're not necessarily URLs. They are often written that way, but they can be any string.

Comment on lines 74 to 80
const capabilities: A2uiClientCapabilities = {
'v0.9': {
supportedCatalogIds: this.catalogs.map(c => c.id),
supportedCatalogIds: this.catalogs.flatMap(c =>
c.aliases ? [c.id, ...c.aliases] : [c.id],
),
},
};

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this would be needed.


// `__dirname` will be `dist/src/v0_9/schema` when run via `node --test dist/**/*.test.js`
const SPEC_DIR_V0_9 = resolve(__dirname, '../../../../../../specification/v0_9/json');
const SPEC_DIR_V0_9 = resolve(__dirname, '../../../../../../specification/v0_9_1/json');

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This effectively ignores the v0_9 directory. We should be testing them independently to make sure they are both still valid.

"types": "./dist/src/v0_9/basic_catalog/index.d.ts",
"default": "./dist/src/v0_9/basic_catalog/index.js"
},
"./v0_9_1": {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This treats the v0_9_1 catalog as if it were the v0_9 catalog. They do have differences, and so this should point to the v0_9_1 catalog.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are there differences in the v0.9 and 0.9.1 catalogs? I thought 0.9.1 was only to release the mimetype update?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, you're right, that's the only difference. Hmm. Maybe in this case the parsing of the catalogs is the same, but I'm thinking of it as "what if we release 0.9.2 and it has extra fields/components?" I don't see that happening for 0.9, but I'm pretty sure it will happen sometime soon, e.g. for v1.1.

I'm wary of just aliasing them like this becoming a pattern.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really think we should hide/remove any actual implementation code for v0.9.1, and just make 0.9 accept both 0.9 and 0.9.1. The aliasing of the catalog id should be a one-time only thing because of the way 0.9.1 was made (that I guess can live in the v0.9 directory only). Maybe we can leave instructions for agents not to replicate that pattern later? :)


super(id, components, functions);
const aliases = options.aliases ?? [
'https://a2ui.org/specification/v0_9_1/catalogs/basic/catalog.json',

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is saying that if you have a v0_9 renderer you can render v0_9_1 content, and while that is often true (they are similar), where they are different, it will fail.

Comment on lines +58 to +67
"./v0_9_1": {
"import": {
"types": "./dist/v0_9/index.d.ts",
"default": "./dist/v0_9/index.js"
},
"require": {
"types": "./dist/v0_9/index.d.cts",
"default": "./dist/v0_9/index.cjs"
}
},

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/cc @gspencergoog for example, I would not recommend adding this alias. People should be importing from v0_9 only.

As we discussed before, IMO the versions of the protocols shouldn't necessarily map to the versions of the renderers.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I agree in this case that they are functionally the same unless you're using A2A. There is one other minor spec change besides the MIME change: in v0.9 the surfaceId had to be globally unique for the entire lifetime of the renderer, and in v0.9.1 it was relaxed so that the surfaceId only needs to be unique among currently active surfaces. But I think that's backward compatible by design.

I'm OK with just using 0.9 code for 0.9.1 (and 0.9) everywhere except A2A support, but we should probably restructure the spec itself so that there isn't duplication and so it makes it abundantly clear that this is a special case and not the norm. I'm pretty sure that from now on dot releases will contain additional features.

@ditman

ditman commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

I took a stab at some of this here: #1993

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.

a2ui/react: v0.9.1 payload compatibility gap in v0_9 runtime

3 participants