From a746cd8267e9c4b30c196680433413d7451b5280 Mon Sep 17 00:00:00 2001 From: Jacob Simionato Date: Fri, 12 Jun 2026 12:50:51 +0930 Subject: [PATCH 1/9] Fix catalogs format --- .../v1_0/catalogs/basic/catalog.json | 470 +++++++++++++----- .../v1_0/catalogs/minimal/catalog.json | 188 +++++-- specification/v1_0/docs/a2ui_protocol.md | 70 +++ .../v1_0/json/client_capabilities.json | 30 +- 4 files changed, 577 insertions(+), 181 deletions(-) diff --git a/specification/v1_0/catalogs/basic/catalog.json b/specification/v1_0/catalogs/basic/catalog.json index 9d9767e14..4af1f8e45 100644 --- a/specification/v1_0/catalogs/basic/catalog.json +++ b/specification/v1_0/catalogs/basic/catalog.json @@ -12,9 +12,6 @@ { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon" }, - { - "$ref": "#/$defs/CatalogComponentCommon" - }, { "type": "object", "properties": { @@ -28,11 +25,26 @@ "variant": { "type": "string", "description": "A hint for the base text style.", - "enum": ["h1", "h2", "h3", "h4", "h5", "caption", "body"], + "enum": [ + "h1", + "h2", + "h3", + "h4", + "h5", + "caption", + "body" + ], "default": "body" + }, + "weight": { + "type": "number", + "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": ["component", "text"] + "required": [ + "component", + "text" + ] } ], "unevaluatedProperties": false @@ -43,9 +55,6 @@ { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon" }, - { - "$ref": "#/$defs/CatalogComponentCommon" - }, { "type": "object", "properties": { @@ -63,17 +72,37 @@ "fit": { "type": "string", "description": "Specifies how the image should be resized to fit its container. This corresponds to the CSS 'object-fit' property.", - "enum": ["contain", "cover", "fill", "none", "scaleDown"], + "enum": [ + "contain", + "cover", + "fill", + "none", + "scaleDown" + ], "default": "fill" }, "variant": { "type": "string", "description": "A hint for the image size and style.", - "enum": ["icon", "avatar", "smallFeature", "mediumFeature", "largeFeature", "header"], + "enum": [ + "icon", + "avatar", + "smallFeature", + "mediumFeature", + "largeFeature", + "header" + ], "default": "mediumFeature" + }, + "weight": { + "type": "number", + "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": ["component", "url"] + "required": [ + "component", + "url" + ] } ], "unevaluatedProperties": false @@ -84,9 +113,6 @@ { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon" }, - { - "$ref": "#/$defs/CatalogComponentCommon" - }, { "type": "object", "properties": { @@ -167,13 +193,22 @@ "type": "string" } }, - "required": ["path"], + "required": [ + "path" + ], "additionalProperties": false } ] + }, + "weight": { + "type": "number", + "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": ["component", "name"] + "required": [ + "component", + "name" + ] } ], "unevaluatedProperties": false @@ -184,9 +219,6 @@ { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon" }, - { - "$ref": "#/$defs/CatalogComponentCommon" - }, { "type": "object", "properties": { @@ -200,9 +232,16 @@ "posterUrl": { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/DynamicString", "description": "The URL of the poster image to display before the video plays." + }, + "weight": { + "type": "number", + "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": ["component", "url"] + "required": [ + "component", + "url" + ] } ], "unevaluatedProperties": false @@ -213,9 +252,6 @@ { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon" }, - { - "$ref": "#/$defs/CatalogComponentCommon" - }, { "type": "object", "properties": { @@ -229,9 +265,16 @@ "description": { "description": "A description of the audio, such as a title or summary.", "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/DynamicString" + }, + "weight": { + "type": "number", + "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": ["component", "url"] + "required": [ + "component", + "url" + ] } ], "unevaluatedProperties": false @@ -242,9 +285,6 @@ { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon" }, - { - "$ref": "#/$defs/CatalogComponentCommon" - }, { "type": "object", "description": "A layout component that arranges its children horizontally. To create a grid layout, nest Columns within this Row.", @@ -273,11 +313,23 @@ "align": { "type": "string", "description": "Defines the alignment of children along the cross axis (vertically). This is similar to the CSS 'align-items' property, but uses camelCase values (e.g., 'start').", - "enum": ["start", "center", "end", "stretch"], + "enum": [ + "start", + "center", + "end", + "stretch" + ], "default": "stretch" + }, + "weight": { + "type": "number", + "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": ["component", "children"] + "required": [ + "component", + "children" + ] } ], "unevaluatedProperties": false @@ -288,9 +340,6 @@ { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon" }, - { - "$ref": "#/$defs/CatalogComponentCommon" - }, { "type": "object", "description": "A layout component that arranges its children vertically. To create a grid layout, nest Rows within this Column.", @@ -319,11 +368,23 @@ "align": { "type": "string", "description": "Defines the alignment of children along the cross axis (horizontally). This is similar to the CSS 'align-items' property.", - "enum": ["center", "end", "start", "stretch"], + "enum": [ + "center", + "end", + "start", + "stretch" + ], "default": "stretch" + }, + "weight": { + "type": "number", + "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": ["component", "children"] + "required": [ + "component", + "children" + ] } ], "unevaluatedProperties": false @@ -334,9 +395,6 @@ { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon" }, - { - "$ref": "#/$defs/CatalogComponentCommon" - }, { "type": "object", "properties": { @@ -350,17 +408,32 @@ "direction": { "type": "string", "description": "The direction in which the list items are laid out.", - "enum": ["vertical", "horizontal"], + "enum": [ + "vertical", + "horizontal" + ], "default": "vertical" }, "align": { "type": "string", "description": "Defines the alignment of children along the cross axis.", - "enum": ["start", "center", "end", "stretch"], + "enum": [ + "start", + "center", + "end", + "stretch" + ], "default": "stretch" + }, + "weight": { + "type": "number", + "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": ["component", "children"] + "required": [ + "component", + "children" + ] } ], "unevaluatedProperties": false @@ -371,9 +444,6 @@ { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon" }, - { - "$ref": "#/$defs/CatalogComponentCommon" - }, { "type": "object", "properties": { @@ -383,9 +453,16 @@ "child": { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentId", "description": "The ID of the single child component to be rendered inside the card. To display multiple elements, you MUST wrap them in a layout component (like Column or Row) and pass that container's ID here. Do NOT pass multiple IDs or a non-existent ID. Do NOT define the child component inline." + }, + "weight": { + "type": "number", + "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": ["component", "child"] + "required": [ + "component", + "child" + ] } ], "unevaluatedProperties": false @@ -396,9 +473,6 @@ { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon" }, - { - "$ref": "#/$defs/CatalogComponentCommon" - }, { "type": "object", "properties": { @@ -421,12 +495,22 @@ "description": "The ID of the child component. Do NOT define the component inline." } }, - "required": ["title", "child"], + "required": [ + "title", + "child" + ], "additionalProperties": false } + }, + "weight": { + "type": "number", + "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": ["component", "tabs"] + "required": [ + "component", + "tabs" + ] } ], "unevaluatedProperties": false @@ -437,9 +521,6 @@ { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon" }, - { - "$ref": "#/$defs/CatalogComponentCommon" - }, { "type": "object", "properties": { @@ -453,9 +534,17 @@ "content": { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentId", "description": "The ID of the component to be displayed inside the modal. Do NOT define the component inline." + }, + "weight": { + "type": "number", + "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": ["component", "trigger", "content"] + "required": [ + "component", + "trigger", + "content" + ] } ], "unevaluatedProperties": false @@ -466,9 +555,6 @@ { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon" }, - { - "$ref": "#/$defs/CatalogComponentCommon" - }, { "type": "object", "properties": { @@ -478,11 +564,20 @@ "axis": { "type": "string", "description": "The orientation of the divider.", - "enum": ["horizontal", "vertical"], + "enum": [ + "horizontal", + "vertical" + ], "default": "horizontal" + }, + "weight": { + "type": "number", + "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": ["component"] + "required": [ + "component" + ] } ], "unevaluatedProperties": false @@ -493,9 +588,6 @@ { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon" }, - { - "$ref": "#/$defs/CatalogComponentCommon" - }, { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/Checkable" }, @@ -512,14 +604,26 @@ "variant": { "type": "string", "description": "A hint for the button style. If omitted, a default button style is used. 'primary' indicates this is the main call-to-action button. 'borderless' means the button has no visual border or background, making its child content appear like a clickable link.", - "enum": ["default", "primary", "borderless"], + "enum": [ + "default", + "primary", + "borderless" + ], "default": "default" }, "action": { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/Action" + }, + "weight": { + "type": "number", + "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": ["component", "child", "action"] + "required": [ + "component", + "child", + "action" + ] } ], "unevaluatedProperties": false @@ -530,9 +634,6 @@ { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon" }, - { - "$ref": "#/$defs/CatalogComponentCommon" - }, { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/Checkable" }, @@ -557,11 +658,23 @@ "variant": { "type": "string", "description": "The type of input field to display.", - "enum": ["longText", "number", "shortText", "obscured"], + "enum": [ + "longText", + "number", + "shortText", + "obscured" + ], "default": "shortText" + }, + "weight": { + "type": "number", + "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": ["component", "label"] + "required": [ + "component", + "label" + ] } ], "unevaluatedProperties": false @@ -572,9 +685,6 @@ { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon" }, - { - "$ref": "#/$defs/CatalogComponentCommon" - }, { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/Checkable" }, @@ -591,9 +701,17 @@ "value": { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/DynamicBoolean", "description": "The current state of the checkbox (true for checked, false for unchecked)." + }, + "weight": { + "type": "number", + "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": ["component", "label", "value"] + "required": [ + "component", + "label", + "value" + ] } ], "unevaluatedProperties": false @@ -604,9 +722,6 @@ { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon" }, - { - "$ref": "#/$defs/CatalogComponentCommon" - }, { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/Checkable" }, @@ -624,7 +739,10 @@ "variant": { "type": "string", "description": "A hint for how the choice picker should be displayed and behave.", - "enum": ["multipleSelection", "mutuallyExclusive"], + "enum": [ + "multipleSelection", + "mutuallyExclusive" + ], "default": "mutuallyExclusive" }, "options": { @@ -642,7 +760,10 @@ "description": "The stable value associated with this option." } }, - "required": ["label", "value"], + "required": [ + "label", + "value" + ], "additionalProperties": false } }, @@ -653,16 +774,27 @@ "displayStyle": { "type": "string", "description": "The display style of the component.", - "enum": ["checkbox", "chips"], + "enum": [ + "checkbox", + "chips" + ], "default": "checkbox" }, "filterable": { "type": "boolean", "description": "If true, displays a search input to filter the options.", "default": false + }, + "weight": { + "type": "number", + "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": ["component", "options", "value"] + "required": [ + "component", + "options", + "value" + ] } ], "unevaluatedProperties": false @@ -673,9 +805,6 @@ { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon" }, - { - "$ref": "#/$defs/CatalogComponentCommon" - }, { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/Checkable" }, @@ -706,9 +835,17 @@ "type": "integer", "minimum": 1, "description": "The number of discrete divisions in the slider range. If specified, the slider will snap to discrete values." + }, + "weight": { + "type": "number", + "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": ["component", "value", "max"] + "required": [ + "component", + "value", + "max" + ] } ], "unevaluatedProperties": false @@ -719,9 +856,6 @@ { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon" }, - { - "$ref": "#/$defs/CatalogComponentCommon" - }, { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/Checkable" }, @@ -800,9 +934,16 @@ "label": { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/DynamicString", "description": "The text label for the input field." + }, + "weight": { + "type": "number", + "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": ["component", "value"] + "required": [ + "component", + "value" + ] } ], "unevaluatedProperties": false @@ -824,11 +965,16 @@ "description": "The value to check." } }, - "required": ["value"], + "required": [ + "value" + ], "additionalProperties": false } }, - "required": ["call", "args"], + "required": [ + "call", + "args" + ], "unevaluatedProperties": false }, "regex": { @@ -850,11 +996,17 @@ "description": "The regex pattern to match against." } }, - "required": ["value", "pattern"], + "required": [ + "value", + "pattern" + ], "unevaluatedProperties": false } }, - "required": ["call", "args"], + "required": [ + "call", + "args" + ], "unevaluatedProperties": false }, "length": { @@ -882,19 +1034,28 @@ "description": "The maximum allowed length." } }, - "required": ["value"], + "required": [ + "value" + ], "anyOf": [ { - "required": ["min"] + "required": [ + "min" + ] }, { - "required": ["max"] + "required": [ + "max" + ] } ], "unevaluatedProperties": false } }, - "required": ["call", "args"], + "required": [ + "call", + "args" + ], "unevaluatedProperties": false }, "numeric": { @@ -920,19 +1081,28 @@ "description": "The maximum allowed value." } }, - "required": ["value"], + "required": [ + "value" + ], "anyOf": [ { - "required": ["min"] + "required": [ + "min" + ] }, { - "required": ["max"] + "required": [ + "max" + ] } ], "unevaluatedProperties": false } }, - "required": ["call", "args"], + "required": [ + "call", + "args" + ], "unevaluatedProperties": false }, "email": { @@ -950,11 +1120,16 @@ "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/DynamicString" } }, - "required": ["value"], + "required": [ + "value" + ], "unevaluatedProperties": false } }, - "required": ["call", "args"], + "required": [ + "call", + "args" + ], "unevaluatedProperties": false }, "formatString": { @@ -972,11 +1147,16 @@ "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/DynamicString" } }, - "required": ["value"], + "required": [ + "value" + ], "unevaluatedProperties": false } }, - "required": ["call", "args"], + "required": [ + "call", + "args" + ], "unevaluatedProperties": false }, "formatNumber": { @@ -1003,11 +1183,16 @@ "description": "Optional. If true, uses locale-specific grouping separators (e.g. '1,000'). If false, returns raw digits (e.g. '1000'). Defaults to true." } }, - "required": ["value"], + "required": [ + "value" + ], "unevaluatedProperties": false } }, - "required": ["call", "args"], + "required": [ + "call", + "args" + ], "unevaluatedProperties": false }, "formatCurrency": { @@ -1038,11 +1223,17 @@ "description": "Optional. If true, uses locale-specific grouping separators (e.g. '1,000'). If false, returns raw digits (e.g. '1000'). Defaults to true." } }, - "required": ["currency", "value"], + "required": [ + "currency", + "value" + ], "unevaluatedProperties": false } }, - "required": ["call", "args"], + "required": [ + "call", + "args" + ], "unevaluatedProperties": false }, "formatDate": { @@ -1065,11 +1256,17 @@ "description": "A Unicode TR35 date pattern string.\n\nToken Reference:\n- Year: 'yy' (26), 'yyyy' (2026)\n- Month: 'M' (1), 'MM' (01), 'MMM' (Jan), 'MMMM' (January)\n- Day: 'd' (1), 'dd' (01), 'E' (Tue), 'EEEE' (Tuesday)\n- Hour (12h): 'h' (1-12), 'hh' (01-12) - requires 'a' for AM/PM\n- Hour (24h): 'H' (0-23), 'HH' (00-23) - Military Time\n- Minute: 'mm' (00-59)\n- Second: 'ss' (00-59)\n- Period: 'a' (AM/PM)\n\nExamples:\n- 'MMM dd, yyyy' -> 'Jan 16, 2026'\n- 'HH:mm' -> '14:30' (Military)\n- 'h:mm a' -> '2:30 PM'\n- 'EEEE, d MMMM' -> 'Friday, 16 January'" } }, - "required": ["format", "value"], + "required": [ + "format", + "value" + ], "unevaluatedProperties": false } }, - "required": ["call", "args"], + "required": [ + "call", + "args" + ], "unevaluatedProperties": false }, "pluralize": { @@ -1112,11 +1309,17 @@ "description": "The default/fallback string (used for general plural cases)." } }, - "required": ["value", "other"], + "required": [ + "value", + "other" + ], "unevaluatedProperties": false } }, - "required": ["call", "args"], + "required": [ + "call", + "args" + ], "unevaluatedProperties": false }, "openUrl": { @@ -1136,11 +1339,16 @@ "description": "The URL to open." } }, - "required": ["url"], + "required": [ + "url" + ], "additionalProperties": false } }, - "required": ["call", "args"], + "required": [ + "call", + "args" + ], "unevaluatedProperties": false }, "and": { @@ -1163,11 +1371,16 @@ "minItems": 2 } }, - "required": ["values"], + "required": [ + "values" + ], "unevaluatedProperties": false } }, - "required": ["call", "args"], + "required": [ + "call", + "args" + ], "unevaluatedProperties": false }, "or": { @@ -1190,11 +1403,16 @@ "minItems": 2 } }, - "required": ["values"], + "required": [ + "values" + ], "unevaluatedProperties": false } }, - "required": ["call", "args"], + "required": [ + "call", + "args" + ], "unevaluatedProperties": false }, "not": { @@ -1213,24 +1431,20 @@ "description": "The boolean value to negate." } }, - "required": ["value"], + "required": [ + "value" + ], "unevaluatedProperties": false } }, - "required": ["call", "args"], + "required": [ + "call", + "args" + ], "unevaluatedProperties": false } }, "$defs": { - "CatalogComponentCommon": { - "type": "object", - "properties": { - "weight": { - "type": "number", - "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." - } - } - }, "surfaceProperties": { "type": "object", "properties": { diff --git a/specification/v1_0/catalogs/minimal/catalog.json b/specification/v1_0/catalogs/minimal/catalog.json index 84d0f1700..47354dce2 100644 --- a/specification/v1_0/catalogs/minimal/catalog.json +++ b/specification/v1_0/catalogs/minimal/catalog.json @@ -8,21 +8,39 @@ "Text": { "type": "object", "allOf": [ - {"$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon"}, - {"$ref": "#/$defs/CatalogComponentCommon"}, + { + "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon" + }, { "type": "object", "properties": { - "component": {"const": "Text"}, + "component": { + "const": "Text" + }, "text": { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/DynamicString" }, "variant": { "type": "string", - "enum": ["h1", "h2", "h3", "h4", "h5", "caption", "body"] + "enum": [ + "h1", + "h2", + "h3", + "h4", + "h5", + "caption", + "body" + ] + }, + "weight": { + "type": "number", + "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": ["component", "text"] + "required": [ + "component", + "text" + ] } ], "unevaluatedProperties": false @@ -30,12 +48,15 @@ "Row": { "type": "object", "allOf": [ - {"$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon"}, - {"$ref": "#/$defs/CatalogComponentCommon"}, + { + "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon" + }, { "type": "object", "properties": { - "component": {"const": "Row"}, + "component": { + "const": "Row" + }, "children": { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ChildList" }, @@ -53,10 +74,22 @@ }, "align": { "type": "string", - "enum": ["start", "center", "end", "stretch"] + "enum": [ + "start", + "center", + "end", + "stretch" + ] + }, + "weight": { + "type": "number", + "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": ["component", "children"] + "required": [ + "component", + "children" + ] } ], "unevaluatedProperties": false @@ -64,12 +97,15 @@ "Column": { "type": "object", "allOf": [ - {"$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon"}, - {"$ref": "#/$defs/CatalogComponentCommon"}, + { + "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon" + }, { "type": "object", "properties": { - "component": {"const": "Column"}, + "component": { + "const": "Column" + }, "children": { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ChildList" }, @@ -87,10 +123,22 @@ }, "align": { "type": "string", - "enum": ["center", "end", "start", "stretch"] + "enum": [ + "center", + "end", + "start", + "stretch" + ] + }, + "weight": { + "type": "number", + "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": ["component", "children"] + "required": [ + "component", + "children" + ] } ], "unevaluatedProperties": false @@ -98,25 +146,41 @@ "Button": { "type": "object", "allOf": [ - {"$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon"}, - {"$ref": "#/$defs/CatalogComponentCommon"}, - {"$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/Checkable"}, + { + "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon" + }, + { + "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/Checkable" + }, { "type": "object", "properties": { - "component": {"const": "Button"}, + "component": { + "const": "Button" + }, "child": { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentId" }, "variant": { "type": "string", - "enum": ["primary", "borderless"] + "enum": [ + "primary", + "borderless" + ] }, "action": { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/Action" + }, + "weight": { + "type": "number", + "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": ["component", "child", "action"] + "required": [ + "component", + "child", + "action" + ] } ], "unevaluatedProperties": false @@ -124,13 +188,18 @@ "TextField": { "type": "object", "allOf": [ - {"$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon"}, - {"$ref": "#/$defs/CatalogComponentCommon"}, - {"$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/Checkable"}, + { + "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon" + }, + { + "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/Checkable" + }, { "type": "object", "properties": { - "component": {"const": "TextField"}, + "component": { + "const": "TextField" + }, "label": { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/DynamicString" }, @@ -139,11 +208,25 @@ }, "variant": { "type": "string", - "enum": ["longText", "number", "shortText", "obscured"] + "enum": [ + "longText", + "number", + "shortText", + "obscured" + ] + }, + "validationRegexp": { + "type": "string" }, - "validationRegexp": {"type": "string"} + "weight": { + "type": "number", + "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." + } }, - "required": ["component", "label"] + "required": [ + "component", + "label" + ] } ], "unevaluatedProperties": false @@ -155,7 +238,9 @@ "description": "Converts an input string to a capitalized version.", "returnType": "string", "properties": { - "call": {"const": "capitalize"}, + "call": { + "const": "capitalize" + }, "args": { "type": "object", "properties": { @@ -163,21 +248,20 @@ "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/DynamicString" } }, - "required": ["value"], + "required": [ + "value" + ], "unevaluatedProperties": false } }, - "required": ["call", "args"], + "required": [ + "call", + "args" + ], "unevaluatedProperties": false } }, "$defs": { - "CatalogComponentCommon": { - "type": "object", - "properties": { - "weight": {"type": "number"} - } - }, "surfaceProperties": { "type": "object", "properties": {}, @@ -185,16 +269,32 @@ }, "anyComponent": { "oneOf": [ - {"$ref": "#/components/Text"}, - {"$ref": "#/components/Row"}, - {"$ref": "#/components/Column"}, - {"$ref": "#/components/Button"}, - {"$ref": "#/components/TextField"} + { + "$ref": "#/components/Text" + }, + { + "$ref": "#/components/Row" + }, + { + "$ref": "#/components/Column" + }, + { + "$ref": "#/components/Button" + }, + { + "$ref": "#/components/TextField" + } ], - "discriminator": {"propertyName": "component"} + "discriminator": { + "propertyName": "component" + } }, "anyFunction": { - "oneOf": [{"$ref": "#/functions/capitalize"}] + "oneOf": [ + { + "$ref": "#/functions/capitalize" + } + ] } } } diff --git a/specification/v1_0/docs/a2ui_protocol.md b/specification/v1_0/docs/a2ui_protocol.md index 598349bc2..84e923c8e 100644 --- a/specification/v1_0/docs/a2ui_protocol.md +++ b/specification/v1_0/docs/a2ui_protocol.md @@ -473,6 +473,76 @@ To ensure complete cross-language compatibility across client SDKs, parsers, and - `1stItem` (violates initial `Nd`) - `submit-form`, `user#name`, `calc$val` (violates `Pattern_Syntax`) +#### Catalog JSON Schema Structural Restrictions + +To ensure catalog schemas can be translated reliably into alternative, LLM-friendly DSL formats (e.g., HTML-like XML, functional, or compact inline formats) and cleanly mapped to type-safe client SDK representations, all v1.0 component and function catalog definitions MUST conform to the following strict structural constraints: + +1. **Strict Top-Level vs. `$defs` Boundary:** + - **Top-Level components and functions:** All component and function schemas MUST be declared directly under the top-level keys `"components"` and `"functions"` respectively. + - **External References inside `$defs`:** Any definition referenced externally (e.g., from the envelope schema `server_to_client.json` or `common_types.json`) MUST reside inside the `"$defs"` object at the catalog root. This strictly includes: + - `surfaceProperties`: Referenced as `catalog.json#/$defs/surfaceProperties`. + - `anyComponent`: Referenced as `catalog.json#/$defs/anyComponent`. + - `anyFunction`: Referenced as `catalog.json#/$defs/anyFunction`. +2. **No Custom `$defs` or Helpers:** + - To prevent unconstrained branching, custom definitions or shared helper schemas inside a catalog are strictly prohibited under `"$defs"`. + - The only allowed keys within the catalog's `"$defs"` object are `anyComponent`, `anyFunction`, and `surfaceProperties`. + - All helper properties (such as the relative layout `weight` property) MUST be inlined directly inside the properties block of each supporting component schema rather than referenced from a shared helper. +3. **Restricted `$ref` Targets:** + - Local `$ref` targets are restricted to referencing the catalog's top-level components or functions (e.g., `#/components/Text`, `#/functions/required`). + - External `$ref` targets MUST reference the standard types inside `common_types.json` (`https://a2ui.org/specification/v1_0/common_types.json#/$defs/...`), limited to the following allowed schemas: + - `ComponentId` + - `ChildList` + - `DynamicString` + - `DynamicNumber` + - `DynamicBoolean` + - `DynamicStringList` + - `DynamicValue` + - `CheckRule` +4. **Mandatory Dynamic Wrappers (No Raw Primitives):** + - Every leaf node in a component's properties or a function's arguments schema MUST be a dynamic type wrapper (e.g., `DynamicString`, `DynamicNumber`, `DynamicBoolean`, `DynamicValue`) instead of a literal raw primitive (e.g., raw `string`, `number`, `boolean`). This guarantees that any slot can be bound to a data model path. +5. **Prompt-Level Mitigation:** + - The increased verbosity of dynamic wrappers is resolved during inference by the prompt generator, which detects shared structures in memory and condenses them into clean, concise instructions for the LLM. + +#### Catalog Conventions (The "Unwritten Rules") + +To ensure automatic parsing and seamless cross-platform SDK bindings, every catalog MUST also follow these core conventions: + +1. **Component Discriminator Rule:** + - Every component schema defined inside the `components` map must have a required property named `component` whose value is a constant (`const`) matching the key under which it is defined. + - Example: The component defined at `components.Text` must declare: + ```json + "properties": { + "component": { + "const": "Text" + } + } + ``` + - This enables route-dispatch matching via the `discriminator` block inside `anyComponent` (designating `"propertyName": "component"`). +2. **Standard Component Structure:** + - All components defined in the `components` object must use an `allOf` structure that combines: + 1. An external reference to the baseline identity and accessibility attributes: + `{"$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon"}` + 2. A local object schema defining the unique properties of that specific component (e.g., its children, variant, specific layouts). +3. **Strict Function Interface Pattern:** + - Every function schema defined inside the `functions` map must validate a wire-level `FunctionCall` object. This requires: + - A `properties` block with a `call` property containing a constant of the function's name (e.g., `"call": { "const": "email" }`). + - An optional `args` property representing arguments (or absent if the function accepts no arguments). + - Mandatory metadata fields outside the strict JSON validation properties to advertise interface details: + - **`returnType`**: Must be a string enum indicating the return type (`string`, `number`, `boolean`, `array`, `object`, `any`, or `void`). + - **`callableFrom`**: Must be a string enum indicating the execution boundary (`clientOnly`, `remoteOnly`, or `clientOrRemote`). If omitted, it defaults to `clientOnly`. +4. **Strict Top-Level Schema Keys:** + - To keep catalog schemas predictable and prevent custom extensions from polluting the global file space, a `catalog.json` file is restricted to the following root-level keys: + - `$schema` + - `$id` + - `title` + - `description` + - `catalogId` + - `instructions` + - `components` + - `functions` + - `$defs` + - No other top-level keys are permitted. + ### UI composition: the adjacency list model The A2UI protocol defines the UI as a flat list of components. The tree structure is built implicitly using ID references. This is known as an adjacency list model. diff --git a/specification/v1_0/json/client_capabilities.json b/specification/v1_0/json/client_capabilities.json index 9c2635076..29dd743e6 100644 --- a/specification/v1_0/json/client_capabilities.json +++ b/specification/v1_0/json/client_capabilities.json @@ -101,7 +101,27 @@ "properties": { "$schema": {"type": "string"}, "$id": {"type": "string"}, - "$defs": {"type": "object"}, + "$defs": { + "type": "object", + "description": "Standardized schema definitions referenced from outside the catalog file.", + "properties": { + "surfaceProperties": { + "title": "A2UI Surface Properties Schema", + "description": "A JSON Schema that defines the catalog's surface properties.", + "$ref": "https://json-schema.org/draft/2020-12/schema" + }, + "anyComponent": { + "type": "object", + "description": "Unified validation schema for all components." + }, + "anyFunction": { + "type": "object", + "description": "Unified validation schema for all functions." + } + }, + "required": ["anyComponent", "anyFunction"], + "additionalProperties": false + }, "title": { "type": "string", "description": "The title of the catalog." @@ -131,14 +151,6 @@ "additionalProperties": { "$ref": "#/$defs/FunctionDefinition" } - }, - "surfaceProperties": { - "title": "A2UI Surface Properties", - "description": "A schema that defines a catalog of A2UI surface properties. Each key is a surface property name, and each value is the JSON schema for that property.", - "type": "object", - "additionalProperties": { - "$ref": "https://json-schema.org/draft/2020-12/schema" - } } }, "required": ["catalogId"], From 3205847ee6f327bb2b0e9f70f59d3d03e7afcaad Mon Sep 17 00:00:00 2001 From: Jacob Simionato Date: Fri, 12 Jun 2026 12:59:51 +0930 Subject: [PATCH 2/9] Address feedback --- specification/v1_0/docs/a2ui_protocol.md | 3 +++ specification/v1_0/json/client_capabilities.json | 10 ++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/specification/v1_0/docs/a2ui_protocol.md b/specification/v1_0/docs/a2ui_protocol.md index 84e923c8e..518cd39ca 100644 --- a/specification/v1_0/docs/a2ui_protocol.md +++ b/specification/v1_0/docs/a2ui_protocol.md @@ -498,6 +498,9 @@ To ensure catalog schemas can be translated reliably into alternative, LLM-frien - `DynamicStringList` - `DynamicValue` - `CheckRule` + - `ComponentCommon` + - `Checkable` + - `Action` 4. **Mandatory Dynamic Wrappers (No Raw Primitives):** - Every leaf node in a component's properties or a function's arguments schema MUST be a dynamic type wrapper (e.g., `DynamicString`, `DynamicNumber`, `DynamicBoolean`, `DynamicValue`) instead of a literal raw primitive (e.g., raw `string`, `number`, `boolean`). This guarantees that any slot can be bound to a data model path. 5. **Prompt-Level Mitigation:** diff --git a/specification/v1_0/json/client_capabilities.json b/specification/v1_0/json/client_capabilities.json index 29dd743e6..3d1a0d1c2 100644 --- a/specification/v1_0/json/client_capabilities.json +++ b/specification/v1_0/json/client_capabilities.json @@ -111,12 +111,14 @@ "$ref": "https://json-schema.org/draft/2020-12/schema" }, "anyComponent": { - "type": "object", - "description": "Unified validation schema for all components." + "title": "A2UI Any Component Schema", + "description": "Unified validation schema for all components.", + "$ref": "https://json-schema.org/draft/2020-12/schema" }, "anyFunction": { - "type": "object", - "description": "Unified validation schema for all functions." + "title": "A2UI Any Function Schema", + "description": "Unified validation schema for all functions.", + "$ref": "https://json-schema.org/draft/2020-12/schema" } }, "required": ["anyComponent", "anyFunction"], From a32a8c16328fac5fa4e3f66add59d2cecb1b22ba Mon Sep 17 00:00:00 2001 From: jacobsimionato Date: Mon, 15 Jun 2026 14:39:04 +0930 Subject: [PATCH 3/9] Update specification/v1_0/docs/a2ui_protocol.md Co-authored-by: Greg Spencer Signed-off-by: jacobsimionato --- specification/v1_0/docs/a2ui_protocol.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/v1_0/docs/a2ui_protocol.md b/specification/v1_0/docs/a2ui_protocol.md index 518cd39ca..3755ca454 100644 --- a/specification/v1_0/docs/a2ui_protocol.md +++ b/specification/v1_0/docs/a2ui_protocol.md @@ -486,7 +486,7 @@ To ensure catalog schemas can be translated reliably into alternative, LLM-frien 2. **No Custom `$defs` or Helpers:** - To prevent unconstrained branching, custom definitions or shared helper schemas inside a catalog are strictly prohibited under `"$defs"`. - The only allowed keys within the catalog's `"$defs"` object are `anyComponent`, `anyFunction`, and `surfaceProperties`. - - All helper properties (such as the relative layout `weight` property) MUST be inlined directly inside the properties block of each supporting component schema rather than referenced from a shared helper. + - All helper properties (such as common properties factored out of catalog items) MUST be inlined directly inside the properties block of each supporting component schema rather than referenced from a shared helper. 3. **Restricted `$ref` Targets:** - Local `$ref` targets are restricted to referencing the catalog's top-level components or functions (e.g., `#/components/Text`, `#/functions/required`). - External `$ref` targets MUST reference the standard types inside `common_types.json` (`https://a2ui.org/specification/v1_0/common_types.json#/$defs/...`), limited to the following allowed schemas: From 1de4c757a9e5c6946ea741b4c1b6ccff36f29fab Mon Sep 17 00:00:00 2001 From: Jacob Simionato Date: Wed, 17 Jun 2026 07:08:12 +0930 Subject: [PATCH 4/9] Fix formatting --- .../v1_0/catalogs/basic/catalog.json | 330 ++++-------------- .../v1_0/catalogs/minimal/catalog.json | 66 +--- 2 files changed, 77 insertions(+), 319 deletions(-) diff --git a/specification/v1_0/catalogs/basic/catalog.json b/specification/v1_0/catalogs/basic/catalog.json index 7894672c7..d0fd59744 100644 --- a/specification/v1_0/catalogs/basic/catalog.json +++ b/specification/v1_0/catalogs/basic/catalog.json @@ -25,10 +25,7 @@ "variant": { "type": "string", "description": "A hint for the base text style.", - "enum": [ - "caption", - "body" - ], + "enum": ["caption", "body"], "default": "body" }, "weight": { @@ -36,10 +33,7 @@ "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": [ - "component", - "text" - ] + "required": ["component", "text"] } ], "unevaluatedProperties": false @@ -67,26 +61,13 @@ "fit": { "type": "string", "description": "Specifies how the image should be resized to fit its container. This corresponds to the CSS 'object-fit' property.", - "enum": [ - "contain", - "cover", - "fill", - "none", - "scaleDown" - ], + "enum": ["contain", "cover", "fill", "none", "scaleDown"], "default": "fill" }, "variant": { "type": "string", "description": "A hint for the image size and style.", - "enum": [ - "icon", - "avatar", - "smallFeature", - "mediumFeature", - "largeFeature", - "header" - ], + "enum": ["icon", "avatar", "smallFeature", "mediumFeature", "largeFeature", "header"], "default": "mediumFeature" }, "weight": { @@ -94,10 +75,7 @@ "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": [ - "component", - "url" - ] + "required": ["component", "url"] } ], "unevaluatedProperties": false @@ -188,9 +166,7 @@ "type": "string" } }, - "required": [ - "path" - ], + "required": ["path"], "additionalProperties": false } ] @@ -200,10 +176,7 @@ "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": [ - "component", - "name" - ] + "required": ["component", "name"] } ], "unevaluatedProperties": false @@ -233,10 +206,7 @@ "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": [ - "component", - "url" - ] + "required": ["component", "url"] } ], "unevaluatedProperties": false @@ -266,10 +236,7 @@ "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": [ - "component", - "url" - ] + "required": ["component", "url"] } ], "unevaluatedProperties": false @@ -308,12 +275,7 @@ "align": { "type": "string", "description": "Defines the alignment of children along the cross axis (vertically). This is similar to the CSS 'align-items' property, but uses camelCase values (e.g., 'start').", - "enum": [ - "start", - "center", - "end", - "stretch" - ], + "enum": ["start", "center", "end", "stretch"], "default": "stretch" }, "weight": { @@ -321,10 +283,7 @@ "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": [ - "component", - "children" - ] + "required": ["component", "children"] } ], "unevaluatedProperties": false @@ -363,12 +322,7 @@ "align": { "type": "string", "description": "Defines the alignment of children along the cross axis (horizontally). This is similar to the CSS 'align-items' property.", - "enum": [ - "center", - "end", - "start", - "stretch" - ], + "enum": ["center", "end", "start", "stretch"], "default": "stretch" }, "weight": { @@ -376,10 +330,7 @@ "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": [ - "component", - "children" - ] + "required": ["component", "children"] } ], "unevaluatedProperties": false @@ -403,21 +354,13 @@ "direction": { "type": "string", "description": "The direction in which the list items are laid out.", - "enum": [ - "vertical", - "horizontal" - ], + "enum": ["vertical", "horizontal"], "default": "vertical" }, "align": { "type": "string", "description": "Defines the alignment of children along the cross axis.", - "enum": [ - "start", - "center", - "end", - "stretch" - ], + "enum": ["start", "center", "end", "stretch"], "default": "stretch" }, "weight": { @@ -425,10 +368,7 @@ "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": [ - "component", - "children" - ] + "required": ["component", "children"] } ], "unevaluatedProperties": false @@ -454,10 +394,7 @@ "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": [ - "component", - "child" - ] + "required": ["component", "child"] } ], "unevaluatedProperties": false @@ -490,10 +427,7 @@ "description": "The ID of the child component. Do NOT define the component inline." } }, - "required": [ - "title", - "child" - ], + "required": ["title", "child"], "additionalProperties": false } }, @@ -502,10 +436,7 @@ "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": [ - "component", - "tabs" - ] + "required": ["component", "tabs"] } ], "unevaluatedProperties": false @@ -535,11 +466,7 @@ "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": [ - "component", - "trigger", - "content" - ] + "required": ["component", "trigger", "content"] } ], "unevaluatedProperties": false @@ -559,10 +486,7 @@ "axis": { "type": "string", "description": "The orientation of the divider.", - "enum": [ - "horizontal", - "vertical" - ], + "enum": ["horizontal", "vertical"], "default": "horizontal" }, "weight": { @@ -570,9 +494,7 @@ "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": [ - "component" - ] + "required": ["component"] } ], "unevaluatedProperties": false @@ -599,11 +521,7 @@ "variant": { "type": "string", "description": "A hint for the button style. If omitted, a default button style is used. 'primary' indicates this is the main call-to-action button. 'borderless' means the button has no visual border or background, making its child content appear like a clickable link.", - "enum": [ - "default", - "primary", - "borderless" - ], + "enum": ["default", "primary", "borderless"], "default": "default" }, "action": { @@ -614,11 +532,7 @@ "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": [ - "component", - "child", - "action" - ] + "required": ["component", "child", "action"] } ], "unevaluatedProperties": false @@ -653,12 +567,7 @@ "variant": { "type": "string", "description": "The type of input field to display.", - "enum": [ - "longText", - "number", - "shortText", - "obscured" - ], + "enum": ["longText", "number", "shortText", "obscured"], "default": "shortText" }, "weight": { @@ -666,10 +575,7 @@ "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": [ - "component", - "label" - ] + "required": ["component", "label"] } ], "unevaluatedProperties": false @@ -702,11 +608,7 @@ "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": [ - "component", - "label", - "value" - ] + "required": ["component", "label", "value"] } ], "unevaluatedProperties": false @@ -734,10 +636,7 @@ "variant": { "type": "string", "description": "A hint for how the choice picker should be displayed and behave.", - "enum": [ - "multipleSelection", - "mutuallyExclusive" - ], + "enum": ["multipleSelection", "mutuallyExclusive"], "default": "mutuallyExclusive" }, "options": { @@ -755,10 +654,7 @@ "description": "The stable value associated with this option." } }, - "required": [ - "label", - "value" - ], + "required": ["label", "value"], "additionalProperties": false } }, @@ -769,10 +665,7 @@ "displayStyle": { "type": "string", "description": "The display style of the component.", - "enum": [ - "checkbox", - "chips" - ], + "enum": ["checkbox", "chips"], "default": "checkbox" }, "filterable": { @@ -785,11 +678,7 @@ "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": [ - "component", - "options", - "value" - ] + "required": ["component", "options", "value"] } ], "unevaluatedProperties": false @@ -836,11 +725,7 @@ "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": [ - "component", - "value", - "max" - ] + "required": ["component", "value", "max"] } ], "unevaluatedProperties": false @@ -935,10 +820,7 @@ "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": [ - "component", - "value" - ] + "required": ["component", "value"] } ], "unevaluatedProperties": false @@ -960,16 +842,11 @@ "description": "The value to check." } }, - "required": [ - "value" - ], + "required": ["value"], "additionalProperties": false } }, - "required": [ - "call", - "args" - ], + "required": ["call", "args"], "unevaluatedProperties": false }, "regex": { @@ -991,17 +868,11 @@ "description": "The regex pattern to match against." } }, - "required": [ - "value", - "pattern" - ], + "required": ["value", "pattern"], "unevaluatedProperties": false } }, - "required": [ - "call", - "args" - ], + "required": ["call", "args"], "unevaluatedProperties": false }, "length": { @@ -1029,28 +900,19 @@ "description": "The maximum allowed length." } }, - "required": [ - "value" - ], + "required": ["value"], "anyOf": [ { - "required": [ - "min" - ] + "required": ["min"] }, { - "required": [ - "max" - ] + "required": ["max"] } ], "unevaluatedProperties": false } }, - "required": [ - "call", - "args" - ], + "required": ["call", "args"], "unevaluatedProperties": false }, "numeric": { @@ -1076,28 +938,19 @@ "description": "The maximum allowed value." } }, - "required": [ - "value" - ], + "required": ["value"], "anyOf": [ { - "required": [ - "min" - ] + "required": ["min"] }, { - "required": [ - "max" - ] + "required": ["max"] } ], "unevaluatedProperties": false } }, - "required": [ - "call", - "args" - ], + "required": ["call", "args"], "unevaluatedProperties": false }, "email": { @@ -1115,16 +968,11 @@ "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/DynamicString" } }, - "required": [ - "value" - ], + "required": ["value"], "unevaluatedProperties": false } }, - "required": [ - "call", - "args" - ], + "required": ["call", "args"], "unevaluatedProperties": false }, "formatString": { @@ -1142,16 +990,11 @@ "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/DynamicString" } }, - "required": [ - "value" - ], + "required": ["value"], "unevaluatedProperties": false } }, - "required": [ - "call", - "args" - ], + "required": ["call", "args"], "unevaluatedProperties": false }, "formatNumber": { @@ -1178,16 +1021,11 @@ "description": "Optional. If true, uses locale-specific grouping separators (e.g. '1,000'). If false, returns raw digits (e.g. '1000'). Defaults to true." } }, - "required": [ - "value" - ], + "required": ["value"], "unevaluatedProperties": false } }, - "required": [ - "call", - "args" - ], + "required": ["call", "args"], "unevaluatedProperties": false }, "formatCurrency": { @@ -1218,17 +1056,11 @@ "description": "Optional. If true, uses locale-specific grouping separators (e.g. '1,000'). If false, returns raw digits (e.g. '1000'). Defaults to true." } }, - "required": [ - "currency", - "value" - ], + "required": ["currency", "value"], "unevaluatedProperties": false } }, - "required": [ - "call", - "args" - ], + "required": ["call", "args"], "unevaluatedProperties": false }, "formatDate": { @@ -1251,17 +1083,11 @@ "description": "A Unicode TR35 date pattern string.\n\nToken Reference:\n- Year: 'yy' (26), 'yyyy' (2026)\n- Month: 'M' (1), 'MM' (01), 'MMM' (Jan), 'MMMM' (January)\n- Day: 'd' (1), 'dd' (01), 'E' (Tue), 'EEEE' (Tuesday)\n- Hour (12h): 'h' (1-12), 'hh' (01-12) - requires 'a' for AM/PM\n- Hour (24h): 'H' (0-23), 'HH' (00-23) - Military Time\n- Minute: 'mm' (00-59)\n- Second: 'ss' (00-59)\n- Period: 'a' (AM/PM)\n\nExamples:\n- 'MMM dd, yyyy' -> 'Jan 16, 2026'\n- 'HH:mm' -> '14:30' (Military)\n- 'h:mm a' -> '2:30 PM'\n- 'EEEE, d MMMM' -> 'Friday, 16 January'" } }, - "required": [ - "format", - "value" - ], + "required": ["format", "value"], "unevaluatedProperties": false } }, - "required": [ - "call", - "args" - ], + "required": ["call", "args"], "unevaluatedProperties": false }, "pluralize": { @@ -1304,17 +1130,11 @@ "description": "The default/fallback string (used for general plural cases)." } }, - "required": [ - "value", - "other" - ], + "required": ["value", "other"], "unevaluatedProperties": false } }, - "required": [ - "call", - "args" - ], + "required": ["call", "args"], "unevaluatedProperties": false }, "openUrl": { @@ -1334,16 +1154,11 @@ "description": "The URL to open." } }, - "required": [ - "url" - ], + "required": ["url"], "additionalProperties": false } }, - "required": [ - "call", - "args" - ], + "required": ["call", "args"], "unevaluatedProperties": false }, "and": { @@ -1366,16 +1181,11 @@ "minItems": 2 } }, - "required": [ - "values" - ], + "required": ["values"], "unevaluatedProperties": false } }, - "required": [ - "call", - "args" - ], + "required": ["call", "args"], "unevaluatedProperties": false }, "or": { @@ -1398,16 +1208,11 @@ "minItems": 2 } }, - "required": [ - "values" - ], + "required": ["values"], "unevaluatedProperties": false } }, - "required": [ - "call", - "args" - ], + "required": ["call", "args"], "unevaluatedProperties": false }, "not": { @@ -1426,16 +1231,11 @@ "description": "The boolean value to negate." } }, - "required": [ - "value" - ], + "required": ["value"], "unevaluatedProperties": false } }, - "required": [ - "call", - "args" - ], + "required": ["call", "args"], "unevaluatedProperties": false } }, diff --git a/specification/v1_0/catalogs/minimal/catalog.json b/specification/v1_0/catalogs/minimal/catalog.json index 0302d69e6..42b29fac3 100644 --- a/specification/v1_0/catalogs/minimal/catalog.json +++ b/specification/v1_0/catalogs/minimal/catalog.json @@ -22,20 +22,14 @@ }, "variant": { "type": "string", - "enum": [ - "caption", - "body" - ] + "enum": ["caption", "body"] }, "weight": { "type": "number", "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": [ - "component", - "text" - ] + "required": ["component", "text"] } ], "unevaluatedProperties": false @@ -69,22 +63,14 @@ }, "align": { "type": "string", - "enum": [ - "start", - "center", - "end", - "stretch" - ] + "enum": ["start", "center", "end", "stretch"] }, "weight": { "type": "number", "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": [ - "component", - "children" - ] + "required": ["component", "children"] } ], "unevaluatedProperties": false @@ -118,22 +104,14 @@ }, "align": { "type": "string", - "enum": [ - "center", - "end", - "start", - "stretch" - ] + "enum": ["center", "end", "start", "stretch"] }, "weight": { "type": "number", "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": [ - "component", - "children" - ] + "required": ["component", "children"] } ], "unevaluatedProperties": false @@ -158,10 +136,7 @@ }, "variant": { "type": "string", - "enum": [ - "primary", - "borderless" - ] + "enum": ["primary", "borderless"] }, "action": { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/Action" @@ -171,11 +146,7 @@ "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": [ - "component", - "child", - "action" - ] + "required": ["component", "child", "action"] } ], "unevaluatedProperties": false @@ -203,12 +174,7 @@ }, "variant": { "type": "string", - "enum": [ - "longText", - "number", - "shortText", - "obscured" - ] + "enum": ["longText", "number", "shortText", "obscured"] }, "validationRegexp": { "type": "string" @@ -218,10 +184,7 @@ "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column." } }, - "required": [ - "component", - "label" - ] + "required": ["component", "label"] } ], "unevaluatedProperties": false @@ -243,16 +206,11 @@ "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/DynamicString" } }, - "required": [ - "value" - ], + "required": ["value"], "unevaluatedProperties": false } }, - "required": [ - "call", - "args" - ], + "required": ["call", "args"], "unevaluatedProperties": false } }, From e6347f84f217447a33c885f3626b5eb27e7564fd Mon Sep 17 00:00:00 2001 From: Jacob Simionato Date: Wed, 17 Jun 2026 07:16:14 +0930 Subject: [PATCH 5/9] Combine sections --- specification/v1_0/docs/a2ui_protocol.md | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/specification/v1_0/docs/a2ui_protocol.md b/specification/v1_0/docs/a2ui_protocol.md index 9ee1eb99a..c0e08d435 100644 --- a/specification/v1_0/docs/a2ui_protocol.md +++ b/specification/v1_0/docs/a2ui_protocol.md @@ -473,9 +473,9 @@ To ensure complete cross-language compatibility across client SDKs, parsers, and - `1stItem` (violates initial `Nd`) - `submit-form`, `user#name`, `calc$val` (violates `Pattern_Syntax`) -#### Catalog JSON Schema Structural Restrictions +#### Catalog Schema Rules and Conventions -To ensure catalog schemas can be translated reliably into alternative, LLM-friendly DSL formats (e.g., HTML-like XML, functional, or compact inline formats) and cleanly mapped to type-safe client SDK representations, all v1.0 component and function catalog definitions MUST conform to the following strict structural constraints: +To ensure catalog schemas can be translated reliably into alternative, LLM-friendly DSL formats (e.g., HTML-like XML, functional, or compact inline formats), cleanly mapped to type-safe client SDK representations, automatically parsed, and bound seamlessly across platforms, all v1.0 component and function catalog definitions MUST conform to the following strict structural rules and conventions: 1. **Strict Top-Level vs. `$defs` Boundary:** - **Top-Level components and functions:** All component and function schemas MUST be declared directly under the top-level keys `"components"` and `"functions"` respectively. @@ -503,16 +503,10 @@ To ensure catalog schemas can be translated reliably into alternative, LLM-frien - `Action` 4. **Mandatory Dynamic Wrappers (No Raw Primitives):** - Every leaf node in a component's properties or a function's arguments schema MUST be a dynamic type wrapper (e.g., `DynamicString`, `DynamicNumber`, `DynamicBoolean`, `DynamicValue`) instead of a literal raw primitive (e.g., raw `string`, `number`, `boolean`). This guarantees that any slot can be bound to a data model path. -5. **Prompt-Level Mitigation:** - - The increased verbosity of dynamic wrappers is resolved during inference by the prompt generator, which detects shared structures in memory and condenses them into clean, concise instructions for the LLM. - -#### Catalog Conventions (The "Unwritten Rules") - -To ensure automatic parsing and seamless cross-platform SDK bindings, every catalog MUST also follow these core conventions: - -1. **Component Discriminator Rule:** + - _Prompt-Level Mitigation:_ The increased verbosity of dynamic wrappers is resolved during inference by the prompt generator, which detects shared structures in memory and condenses them into clean, concise instructions for the LLM. +5. **Component Discriminator Rule:** - Every component schema defined inside the `components` map must have a required property named `component` whose value is a constant (`const`) matching the key under which it is defined. - - Example: The component defined at `components.Text` must declare: + - _Example:_ The component defined at `components.Text` must declare: ```json "properties": { "component": { @@ -520,20 +514,20 @@ To ensure automatic parsing and seamless cross-platform SDK bindings, every cata } } ``` - - This enables route-dispatch matching via the `discriminator` block inside `anyComponent` (designating `"propertyName": "component"`). -2. **Standard Component Structure:** + This enables route-dispatch matching via the `discriminator` block inside `anyComponent` (designating `"propertyName": "component"`). +6. **Standard Component Structure:** - All components defined in the `components` object must use an `allOf` structure that combines: 1. An external reference to the baseline identity and accessibility attributes: `{"$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon"}` 2. A local object schema defining the unique properties of that specific component (e.g., its children, variant, specific layouts). -3. **Strict Function Interface Pattern:** +7. **Strict Function Interface Pattern:** - Every function schema defined inside the `functions` map must validate a wire-level `FunctionCall` object. This requires: - A `properties` block with a `call` property containing a constant of the function's name (e.g., `"call": { "const": "email" }`). - An optional `args` property representing arguments (or absent if the function accepts no arguments). - Mandatory metadata fields outside the strict JSON validation properties to advertise interface details: - **`returnType`**: Must be a string enum indicating the return type (`string`, `number`, `boolean`, `array`, `object`, `any`, or `void`). - **`callableFrom`**: Must be a string enum indicating the execution boundary (`clientOnly`, `remoteOnly`, or `clientOrRemote`). If omitted, it defaults to `clientOnly`. -4. **Strict Top-Level Schema Keys:** +8. **Strict Top-Level Schema Keys:** - To keep catalog schemas predictable and prevent custom extensions from polluting the global file space, a `catalog.json` file is restricted to the following root-level keys: - `$schema` - `$id` From 14e9a3747d47f912d18ae0792ec89ffea12ec418 Mon Sep 17 00:00:00 2001 From: Jacob Simionato Date: Wed, 17 Jun 2026 07:29:51 +0930 Subject: [PATCH 6/9] Add example --- specification/v1_0/docs/a2ui_protocol.md | 115 ++++++++++++++++++++++- 1 file changed, 112 insertions(+), 3 deletions(-) diff --git a/specification/v1_0/docs/a2ui_protocol.md b/specification/v1_0/docs/a2ui_protocol.md index c0e08d435..272c215ad 100644 --- a/specification/v1_0/docs/a2ui_protocol.md +++ b/specification/v1_0/docs/a2ui_protocol.md @@ -475,7 +475,7 @@ To ensure complete cross-language compatibility across client SDKs, parsers, and #### Catalog Schema Rules and Conventions -To ensure catalog schemas can be translated reliably into alternative, LLM-friendly DSL formats (e.g., HTML-like XML, functional, or compact inline formats), cleanly mapped to type-safe client SDK representations, automatically parsed, and bound seamlessly across platforms, all v1.0 component and function catalog definitions MUST conform to the following strict structural rules and conventions: +To ensure catalog schemas can be translated reliably into alternative, LLM-friendly DSL formats (e.g., HTML-like XML, functional, or compact inline formats), cleanly mapped to type-safe client SDK representations, automatically parsed, and bound seamlessly across platforms, all v1.0 component and function catalog definitions MUST conform to the following strict structural constraints and conventions: 1. **Strict Top-Level vs. `$defs` Boundary:** - **Top-Level components and functions:** All component and function schemas MUST be declared directly under the top-level keys `"components"` and `"functions"` respectively. @@ -506,7 +506,7 @@ To ensure catalog schemas can be translated reliably into alternative, LLM-frien - _Prompt-Level Mitigation:_ The increased verbosity of dynamic wrappers is resolved during inference by the prompt generator, which detects shared structures in memory and condenses them into clean, concise instructions for the LLM. 5. **Component Discriminator Rule:** - Every component schema defined inside the `components` map must have a required property named `component` whose value is a constant (`const`) matching the key under which it is defined. - - _Example:_ The component defined at `components.Text` must declare: + - Example: The component defined at `components.Text` must declare: ```json "properties": { "component": { @@ -540,6 +540,115 @@ To ensure catalog schemas can be translated reliably into alternative, LLM-frien - `$defs` - No other top-level keys are permitted. +##### Example Schema Template + +Below is an annotated, fully compliant `catalog.json` schema template (written in JSONC format with comments) representing a visual, complete model of these rules in action: + +```jsonc +{ + // Rule 8: Strict Top-Level Schema Keys + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://a2ui.org/specification/v1_0/catalogs/basic/catalog.json", + "title": "A2UI Basic Catalog Template", + "description": "An annotated example showcasing structural rules and conventions.", + "catalogId": "https://example.com/catalogs/custom-v1", + "instructions": "Design instructions for LLMs when generating layouts under this catalog.", + + // Rule 1: Top-level components declared under top-level "components" map. + "components": { + "Text": { + "type": "object", + // Rule 6: Components must combine ComponentCommon and local properties using "allOf". + "allOf": [ + { + // Rule 3: External references must reference standard types in common_types.json. + "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon", + }, + { + "type": "object", + "properties": { + // Rule 5: Required "component" property must be a constant matching the component key. + "component": { + "const": "Text", + }, + // Rule 4: Every leaf property must be a dynamic wrapper type instead of a raw primitive. + "text": { + "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/DynamicString", + "description": "Text content to display.", + }, + }, + "required": ["component", "text"], + }, + ], + "unevaluatedProperties": false, + }, + }, + + // Rule 1: Top-level functions declared under top-level "functions" map. + "functions": { + "required": { + "type": "object", + "description": "Checks that the value is not null, undefined, or empty.", + // Rule 7: Strict function metadata defined outside the properties block. + "returnType": "boolean", + "callableFrom": "clientOnly", + "properties": { + // Rule 7: Function call schema requires constant with function's name. + "call": { + "const": "required", + }, + "args": { + "type": "object", + "properties": { + "value": { + "description": "The value to check (uses dynamic wrapper target).", + }, + }, + "required": ["value"], + "additionalProperties": false, + }, + }, + "required": ["call", "args"], + "unevaluatedProperties": false, + }, + }, + + // Rule 1 & Rule 2: $defs is restricted strictly to surfaceProperties, anyComponent, and anyFunction. + // Custom definitions or helpers inside a catalog are strictly prohibited under $defs. + "$defs": { + "surfaceProperties": { + "type": "object", + "properties": { + "agentDisplayName": { + "type": "string", + }, + }, + }, + "anyComponent": { + "oneOf": [ + { + // Rule 3: Local refs restricted to top-level components map. + "$ref": "#/components/Text", + }, + ], + "discriminator": { + "propertyName": "component", + }, + }, + "anyFunction": { + "oneOf": [ + { + // Rule 3: Local refs restricted to top-level functions map. + "$ref": "#/functions/required", + }, + ], + }, + }, +} +``` + +```` + ### UI composition: the adjacency list model The A2UI protocol defines the UI as a flat list of components. The tree structure is built implicitly using ID references. This is known as an adjacency list model. @@ -571,7 +680,7 @@ flowchart TD A -- "Parsed and stored" --> D A -- "Parsed and stored" --> E -``` +```` ### Defining actions From 5aefd69151e1a38f36e3d510f6eefb02e7ca398d Mon Sep 17 00:00:00 2001 From: Jacob Simionato Date: Wed, 17 Jun 2026 10:38:30 +0930 Subject: [PATCH 7/9] Remove everything being dynamic --- specification/v1_0/docs/a2ui_protocol.md | 25 +++++++++++------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/specification/v1_0/docs/a2ui_protocol.md b/specification/v1_0/docs/a2ui_protocol.md index 272c215ad..0d345cbb7 100644 --- a/specification/v1_0/docs/a2ui_protocol.md +++ b/specification/v1_0/docs/a2ui_protocol.md @@ -501,10 +501,7 @@ To ensure catalog schemas can be translated reliably into alternative, LLM-frien - `ComponentCommon` - `Checkable` - `Action` -4. **Mandatory Dynamic Wrappers (No Raw Primitives):** - - Every leaf node in a component's properties or a function's arguments schema MUST be a dynamic type wrapper (e.g., `DynamicString`, `DynamicNumber`, `DynamicBoolean`, `DynamicValue`) instead of a literal raw primitive (e.g., raw `string`, `number`, `boolean`). This guarantees that any slot can be bound to a data model path. - - _Prompt-Level Mitigation:_ The increased verbosity of dynamic wrappers is resolved during inference by the prompt generator, which detects shared structures in memory and condenses them into clean, concise instructions for the LLM. -5. **Component Discriminator Rule:** +4. **Component Discriminator Rule:** - Every component schema defined inside the `components` map must have a required property named `component` whose value is a constant (`const`) matching the key under which it is defined. - Example: The component defined at `components.Text` must declare: ```json @@ -515,19 +512,19 @@ To ensure catalog schemas can be translated reliably into alternative, LLM-frien } ``` This enables route-dispatch matching via the `discriminator` block inside `anyComponent` (designating `"propertyName": "component"`). -6. **Standard Component Structure:** +5. **Standard Component Structure:** - All components defined in the `components` object must use an `allOf` structure that combines: 1. An external reference to the baseline identity and accessibility attributes: `{"$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/ComponentCommon"}` 2. A local object schema defining the unique properties of that specific component (e.g., its children, variant, specific layouts). -7. **Strict Function Interface Pattern:** +6. **Strict Function Interface Pattern:** - Every function schema defined inside the `functions` map must validate a wire-level `FunctionCall` object. This requires: - A `properties` block with a `call` property containing a constant of the function's name (e.g., `"call": { "const": "email" }`). - An optional `args` property representing arguments (or absent if the function accepts no arguments). - Mandatory metadata fields outside the strict JSON validation properties to advertise interface details: - **`returnType`**: Must be a string enum indicating the return type (`string`, `number`, `boolean`, `array`, `object`, `any`, or `void`). - **`callableFrom`**: Must be a string enum indicating the execution boundary (`clientOnly`, `remoteOnly`, or `clientOrRemote`). If omitted, it defaults to `clientOnly`. -8. **Strict Top-Level Schema Keys:** +7. **Strict Top-Level Schema Keys:** - To keep catalog schemas predictable and prevent custom extensions from polluting the global file space, a `catalog.json` file is restricted to the following root-level keys: - `$schema` - `$id` @@ -546,7 +543,7 @@ Below is an annotated, fully compliant `catalog.json` schema template (written i ```jsonc { - // Rule 8: Strict Top-Level Schema Keys + // Rule 7: Strict Top-Level Schema Keys "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://a2ui.org/specification/v1_0/catalogs/basic/catalog.json", "title": "A2UI Basic Catalog Template", @@ -558,7 +555,7 @@ Below is an annotated, fully compliant `catalog.json` schema template (written i "components": { "Text": { "type": "object", - // Rule 6: Components must combine ComponentCommon and local properties using "allOf". + // Rule 5: Components must combine ComponentCommon and local properties using "allOf". "allOf": [ { // Rule 3: External references must reference standard types in common_types.json. @@ -567,11 +564,11 @@ Below is an annotated, fully compliant `catalog.json` schema template (written i { "type": "object", "properties": { - // Rule 5: Required "component" property must be a constant matching the component key. + // Rule 4: Required "component" property must be a constant matching the component key. "component": { "const": "Text", }, - // Rule 4: Every leaf property must be a dynamic wrapper type instead of a raw primitive. + // Leaf properties can be standard JSON primitives or Dynamic wrappers "text": { "$ref": "https://a2ui.org/specification/v1_0/common_types.json#/$defs/DynamicString", "description": "Text content to display.", @@ -589,11 +586,11 @@ Below is an annotated, fully compliant `catalog.json` schema template (written i "required": { "type": "object", "description": "Checks that the value is not null, undefined, or empty.", - // Rule 7: Strict function metadata defined outside the properties block. + // Rule 6: Strict function metadata defined outside the properties block. "returnType": "boolean", "callableFrom": "clientOnly", "properties": { - // Rule 7: Function call schema requires constant with function's name. + // Rule 6: Function call schema requires constant with function's name. "call": { "const": "required", }, @@ -601,7 +598,7 @@ Below is an annotated, fully compliant `catalog.json` schema template (written i "type": "object", "properties": { "value": { - "description": "The value to check (uses dynamic wrapper target).", + "description": "The value to check.", }, }, "required": ["value"], From f7b84aecdcbec871f16d541dabf88f953d71d290 Mon Sep 17 00:00:00 2001 From: Jacob Simionato Date: Wed, 17 Jun 2026 10:42:57 +0930 Subject: [PATCH 8/9] Fix comments --- specification/v1_0/docs/a2ui_protocol.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/specification/v1_0/docs/a2ui_protocol.md b/specification/v1_0/docs/a2ui_protocol.md index 0d345cbb7..4f015d397 100644 --- a/specification/v1_0/docs/a2ui_protocol.md +++ b/specification/v1_0/docs/a2ui_protocol.md @@ -644,7 +644,6 @@ Below is an annotated, fully compliant `catalog.json` schema template (written i } ``` -```` ### UI composition: the adjacency list model @@ -677,7 +676,7 @@ flowchart TD A -- "Parsed and stored" --> D A -- "Parsed and stored" --> E -```` +``` ### Defining actions From 5b3d7853af13ed05b1cc9d04d8bd9bdad8122ac6 Mon Sep 17 00:00:00 2001 From: Jacob Simionato Date: Wed, 17 Jun 2026 10:50:43 +0930 Subject: [PATCH 9/9] Fix formatting --- specification/v1_0/docs/a2ui_protocol.md | 1 - 1 file changed, 1 deletion(-) diff --git a/specification/v1_0/docs/a2ui_protocol.md b/specification/v1_0/docs/a2ui_protocol.md index 4f015d397..221297fce 100644 --- a/specification/v1_0/docs/a2ui_protocol.md +++ b/specification/v1_0/docs/a2ui_protocol.md @@ -644,7 +644,6 @@ Below is an annotated, fully compliant `catalog.json` schema template (written i } ``` - ### UI composition: the adjacency list model The A2UI protocol defines the UI as a flat list of components. The tree structure is built implicitly using ID references. This is known as an adjacency list model.