diff --git a/README.md b/README.md index 61dcc845..f4968084 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,19 @@ -# A2UI (Generative UI Language Format) Protocol +# A2UI (Agent to UI) Protocol -This repository contains the specification for the A2UI (Generative UI Language Format) protocol, a JSONL-based, streaming UI protocol designed to be easily generated by Large Language Models (LLMs). +This repository contains the specification for the A2UI protocol, a JSONL-based, streaming UI protocol designed to be easily generated by Large Language Models (LLMs). ## Project Overview The A2UI protocol enables a server to stream a platform-agnostic, abstract UI definition to a client, which then renders it progressively using a native widget set. The core design principles are: -* **LLM-Friendly**: The protocol uses a simple, declarative, and flat structure that is easy for LLMs to generate. -* **Progressive Rendering**: The UI is streamed as a series of JSONL messages, allowing the client to render the UI as it arrives, improving perceived performance. -* **Platform-Agnostic**: The protocol defines an abstract component tree, and the client is responsible for mapping these abstract components to its native widget implementations. -* **Separation of Concerns**: The protocol separates the UI structure (components), the application state (data model), and the client-side widget rendering. +- **LLM-Friendly**: The protocol uses a simple, declarative, and flat structure that is easy for LLMs to generate. +- **Progressive Rendering**: The UI is streamed as a series of JSONL messages, allowing the client to render the UI as it arrives, improving perceived performance. +- **Platform-Agnostic**: The protocol defines an abstract component tree, and the client is responsible for mapping these abstract components to its native widget implementations. +- **Separation of Concerns**: The protocol separates the UI structure (components), the application state (data model), and the client-side widget rendering. ## Repository Layout The repository is structured as follows: -* `docs/`: This directory contains the formal specification for the A2UI protocol (`docs/a2ui_protocol.md`) and related design documents and proposals. -* `specification/json/`: This directory contains the JSON schema files used to validate A2UI protocol messages and client event messages. +- `docs/`: This directory contains the formal specification for the A2UI protocol (`docs/a2ui_protocol.md`) and related design documents and proposals. +- `specification/json/`: This directory contains the JSON schema files used to validate A2UI protocol messages and client event messages. diff --git a/docs/a2ui_protocol.md b/docs/a2ui_protocol.md index 097b341a..2c8c9532 100644 --- a/docs/a2ui_protocol.md +++ b/docs/a2ui_protocol.md @@ -1,4 +1,4 @@ -# A2UI (Generative UI Language Format) Protocol +# A2UI (Agent to UI) Protocol A Specification for a JSONL-Based, Streaming UI Protocol @@ -6,11 +6,11 @@ Created: Sep 19, 2025 ## Design Requirements -The A2UI protocol should be a system where an LLM can stream a platform-agnostic, abstract UI definition to a client, which then renders it progressively using a native widget set. Every major design choice is traced back to the core challenges of LLM generation, perceived performance, and platform independence. +The A2UI (Agent to UI) protocol should be a system where an LLM can stream a platform-agnostic, abstract UI definition to a client, which then renders it progressively using a native widget set. Every major design choice is traced back to the core challenges of LLM generation, perceived performance, and platform independence. ### Requirement: The protocol must be easily generated by a Transformer Large Language Model (LLM) -This is the most critical driver. "LLM-friendliness" is explicitly mentioned. This requirement directly leads to several design choices: +This is the most critical driver. This requirement directly leads to several design choices: Declarative, Simple Structure: The protocol should use a straightforward, declarative format ("this is a column with these children") rather than an imperative one ("now, add a column; then, append a text widget to it"). LLMs excel at generating structured, declarative data. @@ -42,11 +42,11 @@ The system needs a clear, reliable way to handle both server-pushed UI and clien Unidirectional UI Stream: Using a one-way stream (SSE) for UI updates simplifies the client's logic. It only needs to listen and react. This is a more robust pattern for server-push than trying to manage a complex bidirectional channel. -Out-of-Band Event Handling (REST API): Using a standard, stateless REST endpoint for user actions leverages a well-understood, highly scalable, and reliable web technology. It cleanly separates the concerns of UI rendering from event processing. +Event Handling: Event handling is done via an A2A message from the client to the server agent ## Introduction -The A2UI (Generative UI Language Format) Protocol is a protocol designed for rendering user interfaces from a stream of JSON objects sent from a server. Its core philosophy emphasizes a clean separation of UI structure and application data, enabling progressive rendering as the client processes each message. +The A2UI Protocol is a protocol designed for rendering user interfaces from a stream of JSON objects sent from a server. Its core philosophy emphasizes a clean separation of UI structure and application data, enabling progressive rendering as the client processes each message. The protocol is designed to be "LLM-friendly," meaning its structure is declarative and straightforward, making it easy for a generative model to produce. @@ -60,7 +60,7 @@ Communication occurs via a JSON Lines (JSONL) stream. The client parses each lin Client-to-server communication for user interactions is handled separately via a JSON payload sent to a REST API. This message can be one of several types: - `userAction`: Reports a user-initiated action from a component. -- `clientCapabilities`: Informs the server about the client's capabilities, such as the component catalog it supports. +- `clientUiCapabilities`: Informs the server about the client's capabilities, such as the component catalog it supports. - `error`: Reports a client-side error. This keeps the primary data stream unidirectional. @@ -86,7 +86,7 @@ A **Surface** is a contiguous portion of screen real estate into which a A2UI UI For example, in a chat application, each AI-generated response could be rendered into a separate surface within the conversation history. A separate, persistent surface could be used for a side panel that displays related information. -The `surfaceId` is a top-level property on server-to-client messages that directs changes to the correct area. It is used with messages like `surfaceUpdate`, `dataModelUpdate`, and `deleteSurface` to target a specific surface. +The `surfaceId` is a property within each server-to-client message that directs changes to the correct area. It is used with messages like `beginRendering`, `surfaceUpdate`, `dataModelUpdate`, and `deleteSurface` to target a specific surface. ### 1.4. Data Flow Model @@ -95,13 +95,13 @@ The A2UI protocol is composed of a server-to-client stream describing UI and ind 1. **Server Stream:** The server begins sending the JSONL stream over an SSE connection. 2. **Client-Side Buffering:** The client receives messages and buffers them: - - `updateSurface`: Component definitions are stored in a `Map`, organized by `surfaceId`. If a surface doesn't exist, it is created. + - `surfaceUpdate`: Component definitions are stored in a `Map`, organized by `surfaceId`. If a surface doesn't exist, it is created. - `dataModelUpdate`: The client's internal JSON data model is built or updated. 3. **Render Signal:** The server sends a `beginRendering` message with the `root` component's ID. This prevents a "flash of incomplete content." The client buffers incoming components and data but waits for this explicit signal before attempting the first render, ensuring the initial view is coherent. 4. **Client-Side Rendering:** The client, now in a "ready" state, starts at the `root` component. It recursively walks the component tree by looking up component IDs in its buffer. It resolves any data bindings against the data model and uses its `WidgetRegistry` to instantiate native widgets. -5. **User Interaction and Event Handling:** The user interacts with a rendered widget (e.g., taps a button). The client constructs a `userAction` JSON payload, resolving any data bindings from the component's `action.context`. It sends this payload (as part of a larger client event message) to a pre-configured REST API endpoint on the server via a `POST` request. -6. **Dynamic Updates:** The server processes the `userAction`. If the UI needs to change in response, the server sends new `updateSurface` and `dataModelUpdate` messages over the original SSE stream. As these arrive, the client updates its component buffer and data model, and the UI re-renders to reflect the changes. The server can also send `deleteSurface` to remove a UI region. +5. **User Interaction and Event Handling:** The user interacts with a rendered widget (e.g., taps a button). The client constructs a `userAction` JSON payload, resolving any data bindings from the component's `action.context`. It sends this payload to the server via an A2A message. +6. **Dynamic Updates:** The server processes the `userAction`. If the UI needs to change in response, the server sends new `surfaceUpdate` and `dataModelUpdate` messages over the original SSE stream. As these arrive, the client updates its component buffer and data model, and the UI re-renders to reflect the changes. The server can also send `deleteSurface` to remove a UI region. ```mermaid sequenceDiagram @@ -111,8 +111,8 @@ sequenceDiagram Server->>+Client: SSE Connection (JSONL Stream) Client->>Client: 1. Parse JSONL message loop Until 'beginRendering' - Client->>Client: 2a. Process updateSurface (store components in map for surfaceId) - Client->>Client: 2b. Process dataModelUpdate (update data model) + Client->>Client: 2a. Process surfaceUpdate + Client->>Client: 2b. Process dataModelUpdate end Client->>Client: 3. Process beginRendering (rootId: 'root', isReady: true) Note right of Client: 4. Triggers UI build for a surface @@ -123,8 +123,8 @@ sequenceDiagram Note over Client: 8. User interacts with UI (e.g., clicks button) Client->>Client: 9. Construct userAction payload - Client->>+Server: 10. POST /event (Client Event JSON with userAction) - Server-->>-Client: 11. HTTP 200 OK + Client->>+Server: 10. A2A Message sent to server (Client Event JSON with userAction) + Server-->>-Client: 11. OK loop Dynamic Updates in Response to Event Server->>+Client: surfaceUpdate, dataModelUpdate, or deleteSurface (via SSE) @@ -139,15 +139,15 @@ sequenceDiagram The following is a complete, minimal example of a JSONL stream that renders a user profile card. ```jsonl -{"updateSurface": {"components": [{"id": "root", "componentProperties": {"Column": {"children": {"explicitList": ["profile_card"]}}}}]}} -{"updateSurface": {"components": [{"id": "profile_card", "componentProperties": {"Card": {"child": "card_content"}}}]}} -{"updateSurface": {"components": [{"id": "card_content", "componentProperties": {"Column": {"children": {"explicitList": ["header_row", "bio_text"]}}}}]}} -{"updateSurface": {"components": [{"id": "header_row", "componentProperties": {"Row": {"alignment": "center", "children": {"explicitList": ["avatar", "name_column"]}}}}]}} -{"updateSurface": {"components": [{"id": "avatar", "componentProperties": {"Image": {"url": {"literalString": "[https://www.example.com/profile.jpg)"}}}}]}} -{"updateSurface": {"components": [{"id": "name_column", "componentProperties": {"Column": {"alignment": "start", "children": {"explicitList": ["name_text", "handle_text"]}}}}]}} -{"updateSurface": {"components": [{"id": "name_text", "componentProperties": {"Heading": {"level": "3", "text": {"literalString": "Flutter Fan"}}}}]}} -{"updateSurface": {"components": [{"id": "handle_text", "componentProperties": {"Text": {"text": {"literalString": "@flutterdev"}}}}]}} -{"updateSurface": {"components": [{"id": "bio_text", "componentProperties": {"Text": {"text": {"literalString": "Building beautiful apps from a single codebase."}}}}]}} +{"surfaceUpdate": {"components": [{"id": "root", "component": {"Column": {"children": {"explicitList": ["profile_card"]}}}}]}} +{"surfaceUpdate": {"components": [{"id": "profile_card", "component": {"Card": {"child": "card_content"}}}]}} +{"surfaceUpdate": {"components": [{"id": "card_content", "component": {"Column": {"children": {"explicitList": ["header_row", "bio_text"]}}}}]}} +{"surfaceUpdate": {"components": [{"id": "header_row", "component": {"Row": {"alignment": "center", "children": {"explicitList": ["avatar", "name_column"]}}}}]}} +{"surfaceUpdate": {"components": [{"id": "avatar", "component": {"Image": {"url": {"literalString": "[https://www.example.com/profile.jpg)"}}}}]}} +{"surfaceUpdate": {"components": [{"id": "name_column", "component": {"Column": {"alignment": "start", "children": {"explicitList": ["name_text", "handle_text"]}}}}]}} +{"surfaceUpdate": {"components": [{"id": "name_text", "component": {"Heading": {"level": "3", "text": {"literalString": "Flutter Fan"}}}}]}} +{"surfaceUpdate": {"components": [{"id": "handle_text", "component": {"Text": {"text": {"literalString": "@flutterdev"}}}}]}} +{"surfaceUpdate": {"components": [{"id": "bio_text", "component": {"Text": {"text": {"literalString": "Building beautiful apps from a single codebase."}}}}]}} {"dataModelUpdate": {"contents": {}}} {"beginRendering": {"root": "root"}} ``` @@ -158,21 +158,20 @@ A2UI's component model is designed for flexibility, separating the protocol from ### 2.1. The Catalog: Defining Components -Unlike previous versions with a fixed component set, A2UI now defines components in a separate **Catalog**. A catalog is a schema that defines the available component types (e.g., `Row`, `Text`) and their supported properties. This allows for different clients to support different sets of components, including custom ones. The server must generate `updateSurface` messages that conform to the component catalog understood by the client. Clients can inform the server of the catalog they support using the `clientCapabilities` message. +Unlike previous versions with a fixed component set, A2UI now defines components in a **Catalog**. A catalog is a schema that defines the available component types (e.g., `Row`, `Text`) and their supported properties. This allows for different clients to support different sets of components, including custom ones. The server must generate `surfaceUpdate` messages that conform to the component catalog understood by the client. Clients can inform the server of the catalog they support using the `clientUiCapabilities` message. -### 2.2. The `updateSurface` Message +### 2.2. The `surfaceUpdate` Message -This message is the primary way UI structure is defined. It is sent with a top-level `surfaceId` and contains a `components` array. +This message is the primary way UI structure is defined. It contains a `surfaceId` and a `components` array. ```json { - "surfaceId": "main_content_area", - "updateSurface": { + "surfaceUpdate": { + "surfaceId": "main_content_area", "components": [ { "id": "unique-component-id", - "weight": 1.0, - "componentProperties": { + "component": { "Text": { "text": { "literalString": "Hello, World!" } } @@ -180,7 +179,7 @@ This message is the primary way UI structure is defined. It is sent with a top-l }, { "id": "another-component-id", - "componentProperties": { ... } + "component": { ... } } ] } @@ -194,16 +193,16 @@ This message is the primary way UI structure is defined. It is sent with a top-l Each object in the `components` array has the following structure: - `id`: A required, unique string that identifies this specific component instance. This is used for parent-child references. -- `componentProperties`: A required object that defines the component's type and properties. +- `component`: A required object that defines the component's type and properties. -### 2.4.`componentProperties` (Generic Object) +### 2.4.`component` (Generic Object) -On the wire, this object is generic. Its structure is not defined by the core A2UI protocol. Instead, its validation is based on the active **Catalog**. For a given component, it **must** contain exactly one key, where the key is the string name of the component type from the catalog (e.g., `"Text"`, `"Row"`). The value is an object containing the properties for that component, as defined in the catalog. +On the wire, this object is generic. Its structure is not defined by the core A2UI protocol. Instead, its validation is based on the active **Catalog**. It is a wrapper object that **must** contain exactly one key, where the key is the string name of the component type from the catalog (e.g., `"Text"`, `"Row"`). The value is an object containing the properties for that component, as defined in the catalog. **Example:** A `Text` component: ```json -"componentProperties": { +"component": { "Text": { "text": { "literalString": "This is text" } } @@ -213,10 +212,10 @@ On the wire, this object is generic. Its structure is not defined by the core A2 A `Button` component: ```json -"componentProperties": { +"component": { "Button": { "label": { "literalString": "Click Me" }, - "action": { "action": "submit_form" } + "action": { "name": "submit_form" } } } ``` @@ -236,24 +235,27 @@ This model allows the server to send component definitions in any order, as long ```mermaid flowchart TD subgraph "Server Stream (JSONL)" - A("componentUpdate
components: [root, title, button]") + A("surfaceUpdate
components: [root, title, button, button_text]") B("beginRendering
root: 'root'") end subgraph "Client-Side Buffer (Map)" C("root: {id: 'root', type: Column, children: ['title', 'button']}") D("title: {id: 'title', type: Text, text: 'Welcome'}") - E("button: {id: 'button', type: Button, label: 'Go'}") + E("button: {id: 'button', type: Button, child: 'button_text'}") + E_TEXT("button_text: {id: 'button_text', type: Text, text: 'Go'}") end subgraph "Rendered Widget Tree" F(Column) --> G(Text: 'Welcome') - F --> H(Button: 'Go') + F --> H(Button) + H --> I(Text: 'Go') end A -- "Parsed and stored" --> C A -- "Parsed and stored" --> D A -- "Parsed and stored" --> E + A -- "Parsed and stored" --> E_TEXT B -- "Triggers build from buffer" --> F ``` @@ -303,32 +305,21 @@ A2UI enforces a clean separation between the UI's structure (components) and its This message is the only way to modify the client's data model. -- `contents`: The JSON content to be inserted. -- `path`: An optional, dot-separated path string. +- `surfaceId`: The unique identifier for the UI surface this data model update applies to. +- `path`: An optional path to a location within the data model (e.g., 'user.name'). If omitted, the update applies to the root of the data model. +- `contents`: An array of data entries. Each entry must contain a 'key' and exactly one corresponding typed 'value\*' property (e.g. `valueString`, `valueNumber`, `valueBoolean`, `valueList`). - - If `path` is `null` or empty, the `contents` will **completely replace** the entire data model. - - If `path` is provided (e.g., `user.name` or `user.addresses[0].street`), the client will traverse the data model and insert the `contents` at that specific location, creating nested objects/lists as needed. - -#### Example 1: Replacing the root data model - -```json -{ - "dataModelUpdate": { - "contents": { - "user": { "name": "Alice" }, - "posts": [] - } - } -} -``` - -#### Example 2: Updating a specific path +#### Example: Updating the data model ```json { "dataModelUpdate": { - "path": "user.name", - "contents": "Bob" + "surfaceId": "main_content_area", + "path": "user", + "contents": [ + { "key": "name", "valueString": "Bob" }, + { "key": "isVerified", "valueBoolean": true } + ] } } ``` @@ -378,7 +369,7 @@ The client's interpreter is responsible for resolving these paths against the da ## Section 5: Event Handling -While the server-to-client UI definition is a one-way stream, user interactions and other client-side information are communicated back to the server using a separate, out-of-band mechanism. This is typically a standard REST API endpoint where the client sends a `POST` request with a single client event message. +While the server-to-client UI definition is a one-way stream (e.g., over SSE), user interactions are communicated back to the server using an A2A message. ### 5.1. The Client Event Message @@ -390,7 +381,7 @@ This message is sent when the user interacts with a component that has an action The `userAction` object has the following structure: -- `actionName` (string, required): The name of the action, taken directly from the `action.action` property of the component (e.g., "submit_form"). +- `name` (string, required): The name of the action, taken directly from the `action.name` property of the component (e.g., "submit_form"). - `surfaceId` (string, required): The `id` of the surface where the event originated. - `sourceComponentId` (string, required): The `id` of the component that triggered the event (e.g., "my_button"). - `timestamp` (string, required): An ISO 8601 timestamp of when the event occurred (e.g., "2025-09-19T17:01:00Z"). @@ -400,33 +391,78 @@ The process for resolving the `action.context` remains the same: the client iter ### 5.3. The `clientUiCapabilities` Message -This message is sent by the client to inform the server about its capabilities. This is crucial for supporting different component sets. The message must contain exactly one of the following properties: +This message is sent by the client to inform the server about its capabilities. This is crucial for supporting different component sets, allowing the server to generate UI that is compatible with the client. The message must contain exactly one of the following properties: `catalogUri` or `dynamicCatalog`. - `catalogUri`: A URI pointing to a predefined component catalog schema that the client supports. - `dynamicCatalog`: An inline JSON object, conforming to the Catalog Schema, that defines the client's supported components. This is useful for development or for clients with highly custom component sets. +#### `dynamicCatalog` + +The `dynamicCatalog` property allows the client to send an inline JSON object that defines its entire supported component set. This is especially useful for development or for clients with highly custom components. The object must conform to the Catalog Schema, containing `components`. + +- `components`: An object where each key is the name of a component (e.g., `"MyCustomCard"`) and the value is a valid JSON object schema defining the properties for that component. + +**Example of a `clientUiCapabilities` message with a dynamic catalog:** + +```json +{ + "clientUiCapabilities": { + "dynamicCatalog": { + "components": { + "StatusPill": { + "type": "object", + "properties": { + "text": { "$ref": "#/definitions/BoundValue" }, + "statusColor": { "type": "string" } + }, + "required": ["text", "statusColor"] + } + } + } + } +} +``` + ### 5.4. The `error` Message This message provides a feedback mechanism for the server. It is sent when the client encounters an error, for instance, during UI rendering or data binding. The content of the object is flexible and can contain any relevant error information. ### 5.5. Event Flow Example (`userAction`) -1. **Component Definition** (from `updateSurface`): +1. **Component Definition** (from `surfaceUpdate`): ```json { - "id": "submit_btn", - "componentProperties": { - "Button": { - "label": { "literalString": "Submit" }, - "action": { - "action": "submit_form", - "context": [ - { "key": "userInput", "value": { "path": "form.textField" } }, - { "key": "formId", "value": { "literalString": "f-123" } } - ] + "surfaceUpdate": { + "surfaceId": "main_content_area", + "components": [ + { + "id": "submit_btn_text", + "component": { + "Text": { + "text": { "literalString": "Submit" } + } + } + }, + { + "id": "submit_btn", + "component": { + "Button": { + "child": "submit_btn_text", + "action": { + "name": "submit_form", + "context": [ + { + "key": "userInput", + "value": { "path": "form.textField" } + }, + { "key": "formId", "value": { "literalString": "f-123" } } + ] + } + } + } } - } + ] } } ``` @@ -435,8 +471,11 @@ This message provides a feedback mechanism for the server. It is sent when the c ```json { - "form": { - "textField": "User input text" + "dataModelUpdate": { + "surfaceId": "main_content_area", + "form": { + "textField": "User input text" + } } } ``` @@ -448,7 +487,7 @@ This message provides a feedback mechanism for the server. It is sent when the c ```json { "userAction": { - "actionName": "submit_form", + "name": "submit_form", "surfaceId": "main_content_area", "sourceComponentId": "submit_btn", "timestamp": "2025-09-19T17:05:00Z", @@ -460,7 +499,7 @@ This message provides a feedback mechanism for the server. It is sent when the c } ``` -6. **Server Response:** The server processes this event. If the UI needs to change as a result, the server sends new `updateSurface` or `dataModelUpdate` messages over the **separate SSE stream**. +6. **Server Response:** The server processes this event. If the UI needs to change as a result, the server sends new `surfaceUpdate` or `dataModelUpdate` messages over the **separate SSE stream**. ## Section 6: Client-Side Implementation @@ -471,92 +510,792 @@ A robust client-side interpreter for A2UI should be composed of several key comp - **Component Buffer:** A `Map` that stores all component instances by their `id`. This is populated by `componentUpdate` messages. - **Data Model Store:** A `Map` (or similar) that holds the application state. This is built and modified by `dataModelUpdate` messages. - **Interpreter State:** A state machine to track if the client is ready to render (e.g., a `_isReadyToRender` boolean that is set to `true` by `beginRendering`). -- **`**WidgetRegistry**`**:\*\*\*\* A developer-provided map (e.g., `Map`) that associates component type strings ("Row", "Text") with functions that build native widgets. +- **Widget Registry**: A developer-provided map (e.g., `Map`) that associates component type strings ("Row", "Text") with functions that build native widgets. - **Binding Resolver:** A utility that can take a `BoundValue` (e.g., `{ "path": "user.name" }`) and resolve it against the Data Model Store. - **Surface Manager:** Logic to create, update, and delete UI surfaces based on `surfaceId`. - **Event Handler:** A function, exposed to the `WidgetRegistry`, that constructs and sends the client event message (e.g., `userAction`) to the configured REST API endpoint. -## Section 7: Complete A2UI JSON Schema +## Section 7: Complete A2UI Server To Client JSON Schema -This section provides the formal JSON Schema for a single server-to-client message in the A2UI JSONL stream. Each line in the stream must be a valid JSON object that conforms to this schema. Note that the component set is not defined here, but in a separate Catalog schema. +This section provides the formal JSON Schema for a single server-to-client message in the A2UI JSONL stream. Each line in the stream must be a valid JSON object that conforms to this schema. It includes the entire base catalog of components, but the components may be swapped out for other components supported by the client. It is optimized to be able to be generated in structured output mode from various LLMs. ```json { - "title": "A2UI Protocol Message", - "description": "A single message in the A2UI streaming UI protocol. Exactly ONE of the properties in this object must be set, corresponding to the specific message type.", + "title": "A2UI Message Schema", + "description": "Describes a JSON payload for an A2UI message, which is used to dynamically construct and update user interfaces. A message MUST contain exactly ONE of the action properties: 'beginRendering', 'surfaceUpdate', 'dataModelUpdate', or 'deleteSurface'.", "type": "object", "properties": { - "surfaceId": { - "type": "string", - "description": "An ID for the surface that the UI changes should be applied to. If this surface doesn't exist, it will be created. If this is not specified, the default surface will be used." - }, "beginRendering": { - "title": "BeginRendering Message", - "description": "A schema for a BeginRendering message in the A2UI streaming UI protocol. This message signals that the UI can now be rendered and provides initial root component and styling information.", "type": "object", + "description": "Signals the client to begin rendering a surface with a root component and specific styles.", "properties": { + "surfaceId": { + "type": "string", + "description": "The unique identifier for the UI surface to be rendered." + }, "root": { "type": "string", - "description": "The ID of the root component from which rendering should begin. This is a reference to a component instance by its unique ID. This property is REQUIRED." + "description": "The ID of the root component to render." }, "styles": { "type": "object", - "description": "An object containing styling information for the UI, as defined by the active Catalog.", - "additionalProperties": true + "description": "Styling information for the UI.", + "properties": { + "font": { + "type": "string", + "description": "The primary font for the UI." + }, + "primaryColor": { + "type": "string", + "description": "The primary UI color as a hexadecimal code (e.g., '#00BFFF').", + "pattern": "^#[0-9a-fA-F]{6}$" + } + } } }, - "required": ["root"] + "required": ["root", "surfaceId"] }, "surfaceUpdate": { - "title": "UpdateSurface Message", - "description": "A schema for a updateSurface message in the A2UI streaming UI protocol.", "type": "object", + "description": "Updates a surface with a new set of components.", "properties": { + "surfaceId": { + "type": "string", + "description": "The unique identifier for the UI surface to be updated. If you are adding a new surface this *must* be a new, unique identified that has never been used for any existing surfaces shown." + }, "components": { "type": "array", - "description": "A flat list of all component instances available for rendering. Components reference each other by ID. This property is REQUIRED.", + "description": "A list containing all UI components for the surface.", + "minItems": 1, "items": { - "description": "A specific instance of a ComponentType with its own unique ID and properties.", "type": "object", + "description": "Represents a *single* component in a UI widget tree. This component could be one of many supported types.", "properties": { "id": { "type": "string", - "description": "A unique identifier for this component instance. This property is REQUIRED." + "description": "The unique identifier for this component." }, - "componentProperties": { + "component": { "type": "object", - "description": "Defines the properties for the component type, according to the active Catalog.", - "additionalProperties": true + "description": "A wrapper object that MUST contain exactly one key, which is the name of the component type (e.g., 'Heading'). The value is an object containing the properties for that specific component.", + "properties": { + "Heading": { + "type": "object", + "properties": { + "text": { + "type": "object", + "description": "The text content for the heading. This can be a literal string or a reference to a value in the data model ('path', e.g. 'doc.title').", + "properties": { + "literalString": { + "type": "string" + }, + "path": { + "type": "string" + } + } + }, + "level": { + "type": "string", + "description": "The heading level, corresponding to HTML heading tags (e.g., '1' for

, '2' for

).", + "enum": ["1", "2", "3", "4", "5"] + } + }, + "required": ["text"] + }, + "Text": { + "type": "object", + "properties": { + "text": { + "type": "object", + "description": "The text content to display. This can be a literal string or a reference to a value in the data model ('path', e.g. 'hotel.description').", + "properties": { + "literalString": { + "type": "string" + }, + "path": { + "type": "string" + } + } + } + }, + "required": ["text"] + }, + "Image": { + "type": "object", + "properties": { + "url": { + "type": "object", + "description": "The URL of the image to display. This can be a literal string ('literal') or a reference to a value in the data model ('path', e.g. 'thumbnail.url').", + "properties": { + "literalString": { + "type": "string" + }, + "path": { + "type": "string" + } + } + }, + "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", + "scale-down" + ] + } + }, + "required": ["url"] + }, + "Video": { + "type": "object", + "properties": { + "url": { + "type": "object", + "description": "The URL of the video to display. This can be a literal string or a reference to a value in the data model ('path', e.g. 'video.url').", + "properties": { + "literalString": { + "type": "string" + }, + "path": { + "type": "string" + } + } + } + }, + "required": ["url"] + }, + "AudioPlayer": { + "type": "object", + "properties": { + "url": { + "type": "object", + "description": "The URL of the audio to be played. This can be a literal string ('literal') or a reference to a value in the data model ('path', e.g. 'song.url').", + "properties": { + "literalString": { + "type": "string" + }, + "path": { + "type": "string" + } + } + }, + "description": { + "type": "object", + "description": "A description of the audio, such as a title or summary. This can be a literal string or a reference to a value in the data model ('path', e.g. 'song.title').", + "properties": { + "literalString": { + "type": "string" + }, + "path": { + "type": "string" + } + } + } + }, + "required": ["url"] + }, + "Row": { + "type": "object", + "properties": { + "children": { + "type": "object", + "description": "Defines the children. Use 'explicitList' for a fixed set of children, or 'template' to generate children from a data list.", + "properties": { + "explicitList": { + "type": "array", + "items": { + "type": "string" + } + }, + "template": { + "type": "object", + "description": "A template for generating a dynamic list of children from a data model list. `componentId` is the component to use as a template, and `dataBinding` is the path to the list in the data model.", + "properties": { + "componentId": { + "type": "string" + }, + "dataBinding": { + "type": "string" + } + }, + "required": ["componentId", "dataBinding"] + } + } + }, + "distribution": { + "type": "string", + "description": "Defines the arrangement of children along the main axis (horizontally). This corresponds to the CSS 'justify-content' property.", + "enum": [ + "center", + "end", + "spaceAround", + "spaceBetween", + "spaceEvenly", + "start" + ] + }, + "alignment": { + "type": "string", + "description": "Defines the alignment of children along the cross axis (vertically). This corresponds to the CSS 'align-items' property.", + "enum": ["start", "center", "end", "stretch"] + } + }, + "required": ["children"] + }, + "Column": { + "type": "object", + "properties": { + "children": { + "type": "object", + "description": "Defines the children. Use 'explicitList' for a fixed set of children, or 'template' to generate children from a data list.", + "properties": { + "explicitList": { + "type": "array", + "items": { + "type": "string" + } + }, + "template": { + "type": "object", + "description": "A template for generating a dynamic list of children from a data model list. `componentId` is the component to use as a template, and `dataBinding` is the path to the list in the data model.", + "properties": { + "componentId": { + "type": "string" + }, + "dataBinding": { + "type": "string" + } + }, + "required": ["componentId", "dataBinding"] + } + } + }, + "distribution": { + "type": "string", + "description": "Defines the arrangement of children along the main axis (vertically). This corresponds to the CSS 'justify-content' property.", + "enum": [ + "start", + "center", + "end", + "spaceBetween", + "spaceAround", + "spaceEvenly" + ] + }, + "alignment": { + "type": "string", + "description": "Defines the alignment of children along the cross axis (horizontally). This corresponds to the CSS 'align-items' property.", + "enum": ["center", "end", "start", "stretch"] + } + }, + "required": ["children"] + }, + "List": { + "type": "object", + "properties": { + "children": { + "type": "object", + "description": "Defines the children. Use 'explicitList' for a fixed set of children, or 'template' to generate children from a data list.", + "properties": { + "explicitList": { + "type": "array", + "items": { + "type": "string" + } + }, + "template": { + "type": "object", + "description": "A template for generating a dynamic list of children from a data model list. `componentId` is the component to use as a template, and `dataBinding` is the path to the list in the data model.", + "properties": { + "componentId": { + "type": "string" + }, + "dataBinding": { + "type": "string" + } + }, + "required": ["componentId", "dataBinding"] + } + } + }, + "direction": { + "type": "string", + "description": "The direction in which the list items are laid out.", + "enum": ["vertical", "horizontal"] + }, + "alignment": { + "type": "string", + "description": "Defines the alignment of children along the cross axis.", + "enum": ["start", "center", "end", "stretch"] + } + }, + "required": ["children"] + }, + "Card": { + "type": "object", + "properties": { + "child": { + "type": "string", + "description": "The ID of the component to be rendered inside the card." + } + }, + "required": ["child"] + }, + "Tabs": { + "type": "object", + "properties": { + "tabItems": { + "type": "array", + "description": "An array of objects, where each object defines a tab with a title and a child component.", + "items": { + "type": "object", + "properties": { + "title": { + "type": "object", + "description": "The tab title. Defines the value as either a literal value or a path to data model value (e.g. 'options.title').", + "properties": { + "literalString": { + "type": "string" + }, + "path": { + "type": "string" + } + } + }, + "child": { + "type": "string" + } + }, + "required": ["title", "child"] + } + } + }, + "required": ["tabItems"] + }, + "Divider": { + "type": "object", + "properties": { + "axis": { + "type": "string", + "description": "The orientation of the divider.", + "enum": ["horizontal", "vertical"] + } + } + }, + "Modal": { + "type": "object", + "properties": { + "entryPointChild": { + "type": "string", + "description": "The ID of the component that opens the modal when interacted with (e.g., a button)." + }, + "contentChild": { + "type": "string", + "description": "The ID of the component to be displayed inside the modal." + } + }, + "required": ["entryPointChild", "contentChild"] + }, + "Button": { + "type": "object", + "properties": { + "child": { + "type": "string", + "description": "The ID of the component to display in the button, typically a Text component." + }, + "action": { + "type": "object", + "description": "The client-side action to be dispatched when the button is clicked. It includes the action's name and an optional context payload.", + "properties": { + "name": { + "type": "string" + }, + "context": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "object", + "description": "Defines the value to be included in the context as either a literal value or a path to a data model value (e.g. 'user.name').", + "properties": { + "path": { + "type": "string" + }, + "literalString": { + "type": "string" + }, + "literalNumber": { + "type": "number" + }, + "literalBoolean": { + "type": "boolean" + } + } + } + }, + "required": ["key", "value"] + } + } + }, + "required": ["name"] + } + }, + "required": ["child", "action"] + }, + "CheckBox": { + "type": "object", + "properties": { + "label": { + "type": "object", + "description": "The text to display next to the checkbox. Defines the value as either a literal value or a path to data model ('path', e.g. 'option.label').", + "properties": { + "literalString": { + "type": "string" + }, + "path": { + "type": "string" + } + } + }, + "value": { + "type": "object", + "description": "The current state of the checkbox (true for checked, false for unchecked). This can be a literal boolean ('literalBoolean') or a reference to a value in the data model ('path', e.g. 'filter.open').", + "properties": { + "literalBoolean": { + "type": "boolean" + }, + "path": { + "type": "string" + } + } + } + }, + "required": ["label", "value"] + }, + "TextField": { + "type": "object", + "properties": { + "label": { + "type": "object", + "description": "The text label for the input field. This can be a literal string or a reference to a value in the data model ('path, e.g. 'user.name').", + "properties": { + "literalString": { + "type": "string" + }, + "path": { + "type": "string" + } + } + }, + "text": { + "type": "object", + "description": "The value of the text field. This can be a literal string or a reference to a value in the data model ('path', e.g. 'user.name').", + "properties": { + "literalString": { + "type": "string" + }, + "path": { + "type": "string" + } + } + }, + "textFieldType": { + "type": "string", + "description": "The type of input field to display.", + "enum": [ + "date", + "longText", + "number", + "shortText", + "obscured" + ] + }, + "validationRegexp": { + "type": "string", + "description": "A regular expression used for client-side validation of the input." + } + }, + "required": ["label"] + }, + "DateTimeInput": { + "type": "object", + "properties": { + "value": { + "type": "object", + "description": "The selected date and/or time value. This can be a literal string ('literalString') or a reference to a value in the data model ('path', e.g. 'user.dob').", + "properties": { + "literalString": { + "type": "string" + }, + "path": { + "type": "string" + } + } + }, + "enableDate": { + "type": "boolean", + "description": "If true, allows the user to select a date." + }, + "enableTime": { + "type": "boolean", + "description": "If true, allows the user to select a time." + }, + "outputFormat": { + "type": "string", + "description": "The desired format for the output string after a date or time is selected." + } + }, + "required": ["value"] + }, + "MultipleChoice": { + "type": "object", + "properties": { + "selections": { + "type": "object", + "description": "The currently selected values for the component. This can be a literal array of strings or a path to an array in the data model('path', e.g. 'hotel.options').", + "properties": { + "literalArray": { + "type": "array", + "items": { + "type": "string" + } + }, + "path": { + "type": "string" + } + } + }, + "options": { + "type": "array", + "description": "An array of available options for the user to choose from.", + "items": { + "type": "object", + "properties": { + "label": { + "type": "object", + "description": "The text to display for this option. This can be a literal string or a reference to a value in the data model (e.g. 'option.label').", + "properties": { + "literalString": { + "type": "string" + }, + "path": { + "type": "string" + } + } + }, + "value": { + "type": "string", + "description": "The value to be associated with this option when selected." + } + }, + "required": ["label", "value"] + } + }, + "maxAllowedSelections": { + "type": "integer", + "description": "The maximum number of options that the user is allowed to select." + } + }, + "required": ["selections", "options"] + }, + "Slider": { + "type": "object", + "properties": { + "value": { + "type": "object", + "description": "The current value of the slider. This can be a literal number ('literalNumber') or a reference to a value in the data model ('path', e.g. 'restaurant.cost').", + "properties": { + "literalNumber": { + "type": "number" + }, + "path": { + "type": "string" + } + } + }, + "minValue": { + "type": "number", + "description": "The minimum value of the slider." + }, + "maxValue": { + "type": "number", + "description": "The maximum value of the slider." + } + }, + "required": ["value"] + } + } } }, - "required": ["id", "componentProperties"] + "required": ["id", "component"] } } }, - "required": ["components"] + "required": ["surfaceId", "components"] }, "dataModelUpdate": { - "title": "Data model update", - "description": "Sets or replaces the data model at a specified path with new content.", "type": "object", + "description": "Updates the data model for a surface.", "properties": { + "surfaceId": { + "type": "string", + "description": "The unique identifier for the UI surface this data model update applies to." + }, "path": { "type": "string", - "description": "An optional path to a location within the data model where the content should be inserted or replaced. The path is represented as a dot-separated string and can include array indexing (e.g., 'user.addresses[0].street'). If this field is omitted, the entire data model will be replaced with the provided 'contents'." + "description": "An optional path to a location within the data model (e.g., 'user.name'). If omitted, the entire data model will be replaced." }, "contents": { - "description": "The JSON content to be placed at the specified path. This property is REQUIRED. This can be any valid JSON value (object, array, string, number, boolean, or null). The content at the target path will be completely replaced by this new value." + "type": "array", + "description": "An array of data entries. Each entry must contain a 'key' and exactly one corresponding typed 'value*' property.", + "items": { + "type": "object", + "description": "A single data entry. Exactly one 'value*' property should be provided alongside the key.", + "properties": { + "key": { + "type": "string", + "description": "The key for this data entry." + }, + "valueString": { + "type": "string" + }, + "valueNumber": { + "type": "number" + }, + "valueBoolean": { + "type": "boolean" + }, + "valueList": { + "type": "array", + "items": { + "type": "object", + "properties": { + "valueString": { + "type": "string" + }, + "valueNumber": { + "type": "number" + }, + "valueBoolean": { + "type": "boolean" + } + } + } + } + } + } } }, - "required": ["contents"] + "required": ["contents", "surfaceId"] }, "deleteSurface": { - "title": "DeleteSurface Message", - "description": "A schema for a deleteSurface message in the A2UI streaming UI protocol. This message signals that a surface should be removed from the UI.", "type": "object", - "properties": {}, - "required": [] + "description": "Signals the client to delete the surface identified by 'surfaceId'.", + "properties": { + "surfaceId": { + "type": "string", + "description": "The unique identifier for the UI surface to be deleted." + } + }, + "required": ["surfaceId"] } } } ``` + +## Section 8: Client-to-Server JSON Schema + +This section provides the formal JSON Schema for the client-to-server event message. The client sends a single JSON object that must conform to this schema. This doesn’t have to be generated by an LLM, so it can include JSON schema elements that are incompatible with structured output. + +```json +{ + "title": "A2UI Client-to-Server Event Schema", + "description": "Describes a JSON payload for a client-to-server event message.", + "type": "object", + "minProperties": 1, + "maxProperties": 1, + "properties": { + "userAction": { + "type": "object", + "description": "Reports a user-initiated action from a component.", + "properties": { + "name": { + "type": "string", + "description": "The name of the action, taken from the component's action.name property." + }, + "surfaceId": { + "type": "string", + "description": "The id of the surface where the event originated." + }, + "sourceComponentId": { + "type": "string", + "description": "The id of the component that triggered the event." + }, + "timestamp": { + "type": "string", + "format": "date-time", + "description": "An ISO 8601 timestamp of when the event occurred." + }, + "context": { + "type": "object", + "description": "A JSON object containing the key-value pairs from the component's action.context, after resolving all data bindings.", + "additionalProperties": true + } + }, + "required": [ + "name", + "surfaceId", + "sourceComponentId", + "timestamp", + "context" + ] + }, + "clientUiCapabilities": { + "type": "object", + "description": "Informs the server about the client's capabilities, such as the component catalog it supports. Exactly ONE of the properties in this object must be set.", + "properties": { + "catalogUri": { + "type": "string", + "format": "uri", + "description": "A URI pointing to a predefined component catalog schema that the client supports." + }, + "dynamicCatalog": { + "type": "object", + "description": "An inline JSON object that defines the client's supported components.", + "properties": { + "components": { + "type": "object", + "description": "A map where each key is a component name and the value is a JSON Schema defining an object containing its properties.", + "additionalProperties": { + "$ref": "https://json-schema.org/draft/2020-12/schema" + } + } + }, + "required": ["components"] + } + }, + "oneOf": [ + { "required": ["catalogUri"] }, + { "required": ["dynamicCatalog"] } + ] + }, + "error": { + "type": "object", + "description": "Reports a client-side error. The content is flexible.", + "additionalProperties": true + } + }, + "oneOf": [ + { "required": ["userAction"] }, + { "required": ["clientUiCapabilities"] }, + { "required": ["error"] } + ] +} +``` diff --git a/docs/proposals/v0_8.md b/docs/proposals/v0_8.md index f2a0bdf7..a0c4d6f8 100644 --- a/docs/proposals/v0_8.md +++ b/docs/proposals/v0_8.md @@ -1,30 +1,33 @@ # Proposal: A2UI v0.8 -# Aim +## Aim Propose updates to the A2UI protocol so that it fully supports the functionality of the Flutter Gen UI SDK and we can migrate our SDK to be built on top of A2UI. The main new features are: -* Separation of the specific component types into a Catalog object - * Additional Catalog negotiation features to support custom catalogs. -* Addition of the concept of a "surface" which allows the agent to target updates towards specific regions of the UI. -# Requirements -* **Support custom component catalogs**: Clients and servers should be able to use A2UI with a custom component catalog that is known to both of them and is identified by a name. -* **Support dynamically negotiated components**: Clients can dynamically send their local custom component library to the server to use, if the server chooses to support this. This is especially useful for local development workflows, and integrations where there is a high level of trust between agent and client. -* **Provide a strict schema for the LLM**: The LLM should see a A2UI schema which provides the same degree of strictness as A2UI v0.7, i.e. the LLM should see a schema which specifies all the possible parameters of every widget in the catalog. -* **Standardized component catalog format**: We need to standardize the way component catalogs are expressed so that agent logic can easily generate LLM format schemas from them. -* **Multiple surfaces:** The ability for the LLM to choose to render into one or more discrete “surfaces” +- Separation of the specific component types into a Catalog object + - Additional Catalog negotiation features to support custom catalogs. +- Addition of the concept of a "surface" which allows the agent to target updates towards specific regions of the UI. -# Overview +## Requirements + +- **Support custom component catalogs**: Clients and servers should be able to use A2UI with a custom component catalog that is known to both of them and is identified by a name. +- **Support dynamically negotiated components**: Clients can dynamically send their local custom component library to the server to use, if the server chooses to support this. This is especially useful for local development workflows, and integrations where there is a high level of trust between agent and client. +- **Provide a strict schema for the LLM**: The LLM should see a A2UI schema which provides the same degree of strictness as A2UI v0.7, i.e. the LLM should see a schema which specifies all the possible parameters of every widget in the catalog. +- **Standardized component catalog format**: We need to standardize the way component catalogs are expressed so that agent logic can easily generate LLM format schemas from them. +- **Multiple surfaces:** The ability for the LLM to choose to render into one or more discrete “surfaces” + +## Overview The overall direction of this proposal is to extend and modularize the formal A2UI protocol schema to make it more flexible and extensible. By separating the core protocol—the fundamental message types and structures—from the specific UI components and styling rules, we can support a wider range of use cases and client capabilities. This layered approach allows for greater customization while maintaining a consistent foundation for communication between servers and clients. The rationale for this is: -* Make the wire format agnostic to the catalog so that developers can use custom catalogs. This is the core premise of go/genui-sdk-proposal from the Flutter team, so we need this to be able to adopt A2UI in our SDK. -* Allows LLMs to author UIs in a different format to the wire format, because the requirements of the wire format (simple for humans to understand, standard across all agents) are slightly different from the LLM format (easy to for LLM to understand, can be tailored to each agent or model). -# Interaction +- Make the wire format agnostic to the catalog so that developers can use custom catalogs. This is the core premise of go/genui-sdk-proposal from the Flutter team, so we need this to be able to adopt A2UI in our SDK. +- Allows LLMs to author UIs in a different format to the wire format, because the requirements of the wire format (simple for humans to understand, standard across all agents) are slightly different from the LLM format (easy to for LLM to understand, can be tailored to each agent or model). + +## Interaction 1. (optional) Client fetches the server’s agent card to check server capabilities. This will contain the A2A-UI AgentExtension within AgentCapabilities, which will declare what schemas it supports. If the client doesn't do this and requests an unavailable catalog, then the ClientCapabilities request may fail. 2. (optional) If using custom components, the client makes a ClientCapabilities message to the server to specify which catalog to use. If this message is not sent, the agent will use the standard catalog for the particular A2UI protocol version. @@ -34,7 +37,7 @@ The rationale for this is: ## New Concepts -#### Surfaces +### Surfaces A **Surface** is a contiguous portion of screen real estate into which an A2UI UI can be rendered. The protocol introduces the concept of a `surfaceId` to uniquely identify and manage these areas. This allows a single A2UI stream to control multiple, independent UI regions simultaneously. Each surface has a separate root component and a separate hierarchy of components. Each surface has a separate data model, to avoid collision of keys when working with a large number of surfaces. @@ -42,68 +45,61 @@ For example, in a chat application, each AI-generated response could be rendered The `surfaceId` is used in `SurfaceUpdate` messages to direct component changes to the correct area, and the `DeleteSurface` message allows for explicitly removing a surface and its contents from the UI. - ## Updates to schemas and formats - -#### Server-to-Client Protocol Schema (updated) +### Server-to-Client Protocol Schema (updated) A standard protocol that allows agents to send UI to surfaces. This is similar to v0.7, but the proposal is to: -* Make it generic to the component catalog, to allow for custom components. -* Add a **Data Binding Shorthand** to allow initializing the data model and binding to it in a single step. +- Make it generic to the component catalog, to allow for custom components. +- Add a **Data Binding Shorthand** to allow initializing the data model and binding to it in a single step. -#### Client-to-Server Event Schema (updated) +### Client-to-Server Event Schema (updated) The protocol to allow clients to send messages to agents. This includes user-initiated actions, capability declarations, and error reports. -#### Catalog Definition Schema (new) +### Catalog Definition Schema (new) A schema used to represent a catalog of components that are available. This is used to represent the standard catalog and custom catalogs. This is to allow us to split out the specific catalog definition from the protocol schema. - -#### LLM Schema +### LLM Schema The schema that is actually passed to the LLM by the agent, which is specific to the catalog being used. When you combine the generic Client Protocol Schema and the Standard Catalog Definition, you should be able to produce an LLM schema _exactly_ the same as the v0.7 protocol. - -#### AgentExtension format +### AgentExtension format We define how the ‘params’ of the AgentExtension declaration in the Agent Card will be used to declare the capabilities. - ## Updated data objects - -#### Standard Catalog Definition (new) +### Standard Catalog Definition (new) An object which represents the standard A2UI catalog and its styling parameters, conforming to the Catalog Schema. This is the standard catalog of widgets that we agreed on in v0.7 e.g. Text, Heading, Row etc. This remains part of A2UI, as a well-known set of components that agents and clients can opt into to achieve the widest possible compatibility. - -#### Changes to the standard catalog +### Changes to the standard catalog We will update the standard catalog in the following ways: -* **Image fit Property: A new fit property for the Image component to control how the image is resized. -* Markdown in Text: The Text component now officially supports markdown. + +- **Image `fit` Property**: A new `fit` property for the `Image` component to control how the image is resized. +- **Markdown in `Text`**: The `Text` component now officially supports markdown. ## Proposed components -#### Custom Schema Adapter +### Custom Schema Adapter The schema adapter is an agent-side utility that can accept a custom widget catalog and convert it to a schema which allows the LLM to generate `surfaceUpdate` messages for a particular catalog reliably. -The most simple adapter, which will work for Gemini and other common LLMs, will simply take the “components” from a “Catalog description object” and paste it into `updateSurface.components.componentProperties` and takes the “styles” and pastes them into `beginRendering.styles`. This creates a new schema which includes the detailed widget catalog, and can be passed to the LLM. This schema should be almost identical in structure to a v0.7 A2UI schema. This schema is a strict subset of the more generic v0.8 “Server-to-Client Protocol Schema” and thus messages generated this way do formally conform to the protocol spec. +The most simple adapter, which will work for Gemini and other common LLMs, will simply take the “components” from a “Catalog description object” and paste it into `surfaceUpdate.components.componentProperties` and takes the “styles” and pastes them into `beginRendering.styles`. This creates a new schema which includes the detailed widget catalog, and can be passed to the LLM. This schema should be almost identical in structure to a v0.7 A2UI schema. This schema is a strict subset of the more generic v0.8 “Server-to-Client Protocol Schema” and thus messages generated this way do formally conform to the protocol spec. - -# Detailed design +## Detailed design ### Catalog description schema This schema is used to define Catalogs of available components, including the available properties of each component. The schema for this is: -``` +```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "A2UI Catalog", @@ -127,10 +123,7 @@ This schema is used to define Catalogs of available components, including the av } } }, - "required": [ - "components", - "styles" - ] + "required": ["components", "styles"] } ``` @@ -138,13 +131,13 @@ This schema is used to define Catalogs of available components, including the av This is based on the four message types defined in v0.7 but makes the following changes: -* Formally combines the messages into a combined object. This shows how they can be included in an A2A message. -* Remove the `streamHeader` message, because the version of the protocol is [activated](X-A2A-Extensions) by the client using the `X-A2A-Extensions` HTTP header instead, and the version of the catalog is selected using the new `clientCapabilities` upwards message. -* Adds the concept of a `surfaceId` to the top level of the message, and a new `deleteSurface` message. This allows the protocol to manage multiple independent UI surfaces (e.g., a main view and a side panel) over a single connection. The `surfaceId` directs updates to the correct UI area, and `deleteSurface` allows for explicitly closing or dismissing a surface. -* Replaces the detailed `componentProperties` in `componentUpdate` with a generic object. The available components and their properties are no longer defined in the protocol itself but in a separate `Catalog` definition. -* Moves styling definitions out of the core protocol and into the `Catalog`. The `styles` object in the `beginRendering` message is now a generic object, allowing the server to send any key-value pairs that are defined in the active catalog's `styles` section. This makes styling extensible, just like components. +- Formally combines the messages into a combined object. This shows how they can be included in an A2A message. +- Remove the `streamHeader` message, because the version of the protocol is [activated](X-A2A-Extensions) by the client using the `X-A2A-Extensions` HTTP header instead, and the version of the catalog is selected using the new `clientCapabilities` upwards message. +- Adds the concept of a `surfaceId` to the top level of the message, and a new `deleteSurface` message. This allows the protocol to manage multiple independent UI surfaces (e.g., a main view and a side panel) over a single connection. The `surfaceId` directs updates to the correct UI area, and `deleteSurface` allows for explicitly closing or dismissing a surface. +- Replaces the detailed `componentProperties` in `componentUpdate` with a generic object. The available components and their properties are no longer defined in the protocol itself but in a separate `Catalog` definition. +- Moves styling definitions out of the core protocol and into the `Catalog`. The `styles` object in the `beginRendering` message is now a generic object, allowing the server to send any key-value pairs that are defined in the active catalog's `styles` section. This makes styling extensible, just like components. -``` +```json { "title": "A2UI Protocol message", "description": "A message from the server to the client, which can be one of several types.", @@ -168,10 +161,7 @@ This is based on the four message types defined in v0.7 but makes the following "additionalProperties": true } }, - "required": [ - "root", - "type" - ] + "required": ["root", "type"] }, "surfaceUpdate": { "title": "SurfaceUpdate Message", @@ -195,17 +185,11 @@ This is based on the four message types defined in v0.7 but makes the following "additionalProperties": true } }, - "required": [ - "id", - "componentProperties" - ] + "required": ["id", "componentProperties"] } } }, - "required": [ - "components", - "type" - ] + "required": ["components", "type"] }, "dataModelUpdate": { "title": "Data model update", @@ -220,10 +204,7 @@ This is based on the four message types defined in v0.7 but makes the following "description": "The JSON content to be placed at the specified path. This property is REQUIRED. This can be any valid JSON value (object, array, string, number, boolean, or null). The content at the target path will be completely replaced by this new value." } }, - "required": [ - "contents", - "type" - ] + "required": ["contents", "type"] }, "deleteSurface": { "title": "DeleteSurface Message", @@ -240,11 +221,11 @@ This is based on the four message types defined in v0.7 but makes the following The client sends messages to the server to report user actions, declare capabilities, or send error information. The schema defines three main message types: -* `userAction`: Sent when the user interacts with a component that has an action defined. It includes the action name, the `surfaceId` of the surface it originated from, and any resolved context data. -* `clientCapabilities`: Sent by the client to inform the server about its capabilities, such as the component catalog it supports. The client can either reference a known catalog by URI or provide a dynamic catalog definition directly. -* `error`: Sent when the client encounters an error, for instance, during UI rendering or data binding. This provides a feedback mechanism for the server. +- `userAction`: Sent when the user interacts with a component that has an action defined. It includes the action name, the `surfaceId` of the surface it originated from, and any resolved context data. +- `clientCapabilities`: Sent by the client to inform the server about its capabilities, such as the component catalog it supports. The client can either reference a known catalog by URI or provide a dynamic catalog definition directly. +- `error`: Sent when the client encounters an error, for instance, during UI rendering or data binding. This provides a feedback mechanism for the server. -``` +```json { "title": "A2UI Client Event Message", "description": "A message sent from the client to the server. Exactly ONE of the properties in this object must be set.", @@ -278,12 +259,7 @@ The client sends messages to the server to report user actions, declare capabili "additionalProperties": true } }, - "required": [ - "actionName", - "sourceComponentId", - "timestamp", - "context" - ] + "required": ["actionName", "sourceComponentId", "timestamp", "context"] }, "clientUiCapabilities": { "title": "Client Capabilities", @@ -300,14 +276,10 @@ The client sends messages to the server to report user actions, declare capabili }, "oneOf": [ { - "required": [ - "catalogUri" - ] + "required": ["catalogUri"] }, { - "required": [ - "dynamicCatalog" - ] + "required": ["dynamicCatalog"] } ] }, @@ -326,8 +298,9 @@ The client sends messages to the server to report user actions, declare capabili To simplify the generation of UIs, especially for LLMs, we propose a shorthand for data binding that also allows for initializing the data model. Currently, to display a dynamic value that has a default, the server must send two separate messages: + 1. A `dataModelUpdate` message to set the initial value in the data model. -2. An `updateSurface` message with a component that has a property bound to that data model path. +2. An `surfaceUpdate` message with a component that has a property bound to that data model path. The proposed shorthand combines these two steps. When a component property that accepts a `BoundValue` (e.g., `text`, `value`) is defined with _both_ a `path` and a `literal*` value (e.g., `literalString`), it should be interpreted as follows: @@ -337,14 +310,29 @@ The proposed shorthand combines these two steps. When a component property that **Example:** Instead of sending two messages: -``` + +```json {"dataModelUpdate": {"path": "form.name", "contents": "John Doe"}} -{"updateSurface": {"components": [{"id": "name_field", "componentProperties": {"TextField": {"text": {"path": "form.name"}}}}]}} +{"surfaceUpdate": {"components": [{"id": "name_field", "componentProperties": {"TextField": {"text": {"path": "form.name"}}}}]}} ``` The server can send a single message: -``` -{"updateSurface": {"components": [{"id": "name_field", "componentProperties": {"TextField": {"text": {"path": "form.name", "literalString": "John Doe"}}}}]}} + +```json +{ + "surfaceUpdate": { + "components": [ + { + "id": "name_field", + "componentProperties": { + "TextField": { + "text": { "path": "form.name", "literalString": "John Doe" } + } + } + } + ] + } +} ``` This reduces verbosity and simplifies the logic for the server-side generation, making the protocol more "LLM-friendly". The client implementation must ensure that this implicit data model update happens before the component is rendered. This change does not alter the schema of the `BoundValue` object, but rather clarifies the client's expected behavior when both properties are present. @@ -354,30 +342,31 @@ This reduces verbosity and simplifies the logic for the server-side generation, To provide more control over how images are displayed, the `Image` component will be updated to include a `fit` property. This property is a string enum that determines how the image should be resized to fit its container. The possible values are derived from the concepts of `BoxFit` in Flutter and `object-fit` in CSS. The supported values are: -* `fill`: (Default) The image is resized to fill the container, without preserving the aspect ratio. -* `cover`: The image preserves its aspect ratio and fills the container, cropping any overflowing content. -* `contain`: The image preserves its aspect ratio and is scaled to fit inside the container, which may leave empty space. -* `none`: The image is not resized at all. -* `scale-down`: The image is scaled down to the smaller of `none` or `contain`. -This addition will be reflected in the standard catalog and the LLM schema. +- `fill`: (Default) The image is resized to fill the container, without preserving the aspect ratio. +- `cover`: The image preserves its aspect ratio and fills the container, cropping any overflowing content. +- `contain`: The image preserves its aspect ratio and is scaled to fit inside the container, which may leave empty space. +- `none`: The image is not resized at all. +- `scale-down`: The image is scaled down to the smaller of `none` or `contain`. +This addition will be reflected in the standard catalog and the LLM schema. ### Markdown Support in `Text` Component To enhance the richness of the content that can be displayed, the `Text` component will now officially support markdown. The `text` property of the `Text` component will be updated to clarify that its content will be rendered as markdown, allowing for formatting such as bold, italics, lists, and links. -# Extensions +## Extensions -## Formalized AgentCard AgentExtension example +### Formalized AgentCard AgentExtension example AgentCards can specify a list of [AgentExtensions](https://a2a-protocol.org/dev/topics/extensions/#extension-declaration) within their AgentCapabilities. We could define how an AgentExtension can use parameters to declare what schemas it supports, so that a client can understand whether it can connect to it and what schemas it can choose from. The AgentExtension declaration includes params: -* `supportedSchemas`: defines which UI schema versions are supported by the agent. -* `acceptsDynamicSchemas`: defines whether the agent can accept dynamic schemas propagated from the client, e.g. details of additional custom components that can be used. -``` +- `supportedSchemas`: defines which UI schema versions are supported by the agent. +- `acceptsDynamicSchemas`: defines whether the agent can accept dynamic schemas propagated from the client, e.g. details of additional custom components that can be used. + +```json { "name": "Restaurant finder", "capabilities": { @@ -404,9 +393,9 @@ The standard catalog contains some subschemas which would benefit from being reu We could separate out these subschemas into individual files with uris that can be referenced by custom schemas, to ensure consistency of structure. This consistency would make it easier to implement renderers that support custom components, or generic validators which can parse LLM output and check that the references between components are valid etc without attempting actual rendering. -#### stringReference.json +### stringReference.json -``` +```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://raw.githubusercontent.com/google/a2ui/refs/heads/main/schemas/v0.1/stringReference.json", @@ -430,8 +419,7 @@ We could separate out these subschemas into individual files with uris that can As above, but with: - -``` +```json "literalBoolean": { "type": "boolean" } ``` @@ -439,7 +427,7 @@ As above, but with: As above, but with: -``` +```json "literalNumber": { "type": "number" } @@ -447,7 +435,7 @@ As above, but with: #### componentReference.json -``` +```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://raw.githubusercontent.com/google/a2ui/refs/heads/main/schemas/v0.1/componentReference.json", @@ -459,7 +447,7 @@ As above, but with: #### componentArrayReference.json -``` +```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://raw.githubusercontent.com/google/a2ui/refs/heads/main/schemas/v0.1/componentArrayReference.json", @@ -487,10 +475,7 @@ As above, but with: "description": "A data binding reference to a list within the data model (e.g., '/user/posts')." } }, - "required": [ - "componentId", - "dataBinding" - ] + "required": ["componentId", "dataBinding"] } } } @@ -498,7 +483,7 @@ As above, but with: #### action.json -``` +```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://raw.githubusercontent.com/google/a2ui/refs/heads/main/schemas/v0.1/action.json", @@ -540,15 +525,10 @@ As above, but with: } } }, - "required": [ - "key", - "value" - ] + "required": ["key", "value"] } } }, - "required": [ - "action" - ] + "required": ["action"] } ``` diff --git a/specification/json/catalog_description_schema.json b/specification/json/catalog_description_schema.json deleted file mode 100644 index 928a612f..00000000 --- a/specification/json/catalog_description_schema.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "title": "A2UI Catalog Description Schema", - "description": "A schema for a custom Catalog Description including A2UI components and styles.", - "type": "object", - "properties": { - "components": { - "title": "A2UI Components", - "description": "A schema that defines a catalog of A2UI components. Each key is a component name, and each value is the JSON schema for that component's properties.", - "type": "object", - "additionalProperties": { - "$ref": "https://json-schema.org/draft/2020-12/schema" - } - }, - "styles": { - "title": "A2UI Styles", - "description": "A schema that defines a catalog of A2UI styles. Each key is a style name, and each value is the JSON schema for that style's properties.", - "type": "object", - "additionalProperties": { - "$ref": "https://json-schema.org/draft/2020-12/schema" - } - } - }, - "required": [ - "components", - "styles" - ] -} - diff --git a/specification/json/client_event_schema.json b/specification/json/client_event_schema.json deleted file mode 100644 index f3f29352..00000000 --- a/specification/json/client_event_schema.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "title": "A2UI Client Event Message", - "description": "A message sent from the client to the server. Exactly ONE of the properties in this object must be set.", - "type": "object", - "properties": { - "userAction": { - "title": "User Action", - "description": "Represents a user-initiated action, sent from the client to the server.", - "type": "object", - "properties": { - "actionName": { - "type": "string", - "description": "The name of the action, taken from the component's `action.action` property." - }, - "surfaceId": { - "type": "string", - "description": "The `id` of the surface that the event originated from." - }, - "sourceComponentId": { - "type": "string", - "description": "The `id` of the component that triggered the event." - }, - "timestamp": { - "type": "string", - "format": "date-time", - "description": "An ISO 8601 timestamp of when the event occurred." - }, - "context": { - "type": "object", - "description": "A JSON object containing the key-value pairs from the component's `action.context`, after resolving all data bindings.", - "additionalProperties": true - } - }, - "required": [ - "actionName", - "sourceComponentId", - "timestamp", - "context" - ] - }, - "clientUiCapabilities": { - "title": "Client Capabilities", - "description": "A message from the client describing its capabilities, such as the component catalog it supports. Exactly ONE of the properties in this object must be set.", - "type": "object", - "properties": { - "catalogUri": { - "type": "string", - "description": "A URI pointing to a predefined component catalog schema." - }, - "dynamicCatalog": { - "$ref": "https://raw.githubusercontent.com/google/a2ui/refs/heads/main/schemas/v0.1/catalog.json" - } - }, - "oneOf": [ - { - "required": [ - "catalogUri" - ] - }, - { - "required": [ - "dynamicCatalog" - ] - } - ] - }, - "error": { - "title": "Client Error", - "description": "A message from the client indicating an error occurred, for example, during UI rendering.", - "type": "object", - "additionalProperties": true - } - } -} - diff --git a/specification/json/client_to_server.json b/specification/json/client_to_server.json new file mode 100644 index 00000000..5ed5b6d9 --- /dev/null +++ b/specification/json/client_to_server.json @@ -0,0 +1,83 @@ +{ + "title": "A2UI (Agent to UI) Client-to-Server Event Schema", + "description": "Describes a JSON payload for a client-to-server event message.", + "type": "object", + "minProperties": 1, + "maxProperties": 1, + "properties": { + "userAction": { + "type": "object", + "description": "Reports a user-initiated action from a component.", + "properties": { + "name": { + "type": "string", + "description": "The name of the action, taken from the component's action.name property." + }, + "surfaceId": { + "type": "string", + "description": "The id of the surface where the event originated." + }, + "sourceComponentId": { + "type": "string", + "description": "The id of the component that triggered the event." + }, + "timestamp": { + "type": "string", + "format": "date-time", + "description": "An ISO 8601 timestamp of when the event occurred." + }, + "context": { + "type": "object", + "description": "A JSON object containing the key-value pairs from the component's action.context, after resolving all data bindings.", + "additionalProperties": true + } + }, + "required": [ + "name", + "surfaceId", + "sourceComponentId", + "timestamp", + "context" + ] + }, + "clientUiCapabilities": { + "type": "object", + "description": "Informs the server about the client's capabilities, such as the component catalog it supports. Exactly ONE of the properties in this object must be set.", + "properties": { + "catalogUri": { + "type": "string", + "format": "uri", + "description": "A URI pointing to a predefined component catalog schema that the server advertized, and the client supports." + }, + "dynamicCatalog": { + "type": "object", + "description": "An inline JSON object that defines the client's supported components.", + "properties": { + "components": { + "type": "object", + "description": "A map where each key is a component name and the value is a JSON Schema defining an object containing its properties.", + "additionalProperties": { + "$ref": "https://json-schema.org/draft/2020-12/schema" + } + } + }, + "required": ["components"] + } + }, + "oneOf": [ + { "required": ["catalogUri"] }, + { "required": ["dynamicCatalog"] } + ] + }, + "error": { + "type": "object", + "description": "Reports a client-side error. The content is flexible.", + "additionalProperties": true + } + }, + "oneOf": [ + { "required": ["userAction"] }, + { "required": ["clientUiCapabilities"] }, + { "required": ["error"] } + ] +} diff --git a/specification/json/protocol_schema.json b/specification/json/protocol_schema.json deleted file mode 100644 index 1a96ed5c..00000000 --- a/specification/json/protocol_schema.json +++ /dev/null @@ -1,87 +0,0 @@ -{ - "title": "A2UI Protocol message", - "description": "A message from the server to the client, which can be one of several types.", - "properties": { - "surfaceId": { - "type": "string", - "description": "An ID for the surface that the UI changes should be applied to. If this surface doesn't exist, it will be created. If this is not specified, the default surface will be used." - }, - "beginRendering": { - "title": "BeginRendering Message", - "description": "A schema for a BeginRendering message in the A2A streaming UI protocol. This message signals that the UI can now be rendered and provides initial root component and styling information.", - "type": "object", - "properties": { - "root": { - "type": "string", - "description": "The ID of the root component from which rendering should begin. This is a reference to a component instance by its unique ID. This property is REQUIRED." - }, - "styles": { - "type": "object", - "description": "An object containing styling information for the UI.", - "additionalProperties": true - } - }, - "required": [ - "root" - ] - }, - "surfaceUpdate": { - "title": "SurfaceUpdate Message", - "description": "A schema for a SurfaceUpdate message in the A2A streaming UI protocol.", - "type": "object", - "properties": { - "components": { - "type": "array", - "description": "A flat list of all component instances available for rendering. Components reference each other by ID. This property is REQUIRED.", - "items": { - "description": "A specific instance of a ComponentType with its own unique ID and properties.", - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "A unique identifier for this component instance. This property is REQUIRED." - }, - "componentProperties": { - "type": "object", - "description": "Defines the properties for the component type.", - "additionalProperties": true - } - }, - "required": [ - "id", - "componentProperties" - ] - } - } - }, - "required": [ - "components" - ] - }, - "dataModelUpdate": { - "title": "Data model update", - "description": "Sets or replaces the data model at a specified path with new content.", - "type": "object", - "properties": { - "path": { - "type": "string", - "description": "An optional path to a location within the data model where the content should be inserted or replaced. The path is represented as a dot-separated string and can include array indexing (e.g., 'user.addresses[0].street'). If this field is omitted, the entire data model will be replaced with the provided 'contents'." - }, - "contents": { - "description": "The JSON content to be placed at the specified path. This property is REQUIRED. This can be any valid JSON value (object, array, string, number, boolean, or null). The content at the target path will be completely replaced by this new value." - } - }, - "required": [ - "contents" - ] - }, - "surfaceDeletion": { - "title": "SurfaceDeletion Message", - "description": "A schema for a SurfaceDeletion message in the A2A streaming UI protocol. This message signals that a surface should be removed from the UI.", - "type": "object", - "properties": {}, - "required": [] - } - } -} - diff --git a/specification/json/protocol_schema_llm.json b/specification/json/server_to_client.json similarity index 96% rename from specification/json/protocol_schema_llm.json rename to specification/json/server_to_client.json index 992259bf..8decfc45 100644 --- a/specification/json/protocol_schema_llm.json +++ b/specification/json/server_to_client.json @@ -1,6 +1,6 @@ { "title": "A2UI Message Schema", - "description": "Describes a JSON payload for an A2UI message, which is used to dynamically construct and update user interfaces. A message MUST contain exactly ONE of the action properties: 'beginRendering', 'surfaceUpdate', 'dataModelUpdate', or 'surfaceDeletion'.", + "description": "Describes a JSON payload for an A2UI (Agent to UI) message, which is used to dynamically construct and update user interfaces. A message MUST contain exactly ONE of the action properties: 'beginRendering', 'surfaceUpdate', 'dataModelUpdate', or 'deleteSurface'.", "type": "object", "properties": { "beginRendering": { @@ -23,10 +23,6 @@ "type": "string", "description": "The primary font for the UI." }, - "logoUrl": { - "type": "string", - "description": "A URL for the logo image." - }, "primaryColor": { "type": "string", "description": "The primary UI color as a hexadecimal code (e.g., '#00BFFF').", @@ -639,26 +635,39 @@ "description": "An array of data entries. Each entry must contain a 'key' and exactly one corresponding typed 'value*' property.", "items": { "type": "object", - "description": "A single data entry. Exactly one 'value_' property should be provided alongside the key.", + "description": "A single data entry. Exactly one 'value*' property should be provided alongside the key.", "properties": { "key": { "type": "string", "description": "The key for this data entry." }, "valueString": { - "type": "string", - "description": "A string value." + "type": "string" }, "valueNumber": { - "type": "number", - "description": "A number value." + "type": "number" }, "valueBoolean": { - "type": "boolean", - "description": "A boolean value." + "type": "boolean" + }, + "valueList": { + "type": "array", + "items": { + "type": "object", + "properties": { + "valueString": { + "type": "string" + }, + "valueNumber": { + "type": "number" + }, + "valueBoolean": { + "type": "boolean" + } + } + } } - }, - "required": ["key"] + } } } }, diff --git a/specification/json/standard_catalog_description.json b/specification/json/standard_catalog_description.json deleted file mode 100644 index af1389d3..00000000 --- a/specification/json/standard_catalog_description.json +++ /dev/null @@ -1,708 +0,0 @@ -{ - "components": { - "Heading": { - "type": "object", - "properties": { - "text": { - "type": "object", - "description": "The value for this property. Can be a literal value or a data binding path. If both `path` and a `literal` value are provided, the client will initialize the data model at the given path with the literal value and then bind to that path.", - "properties": { - "path": { - "type": "string", - "description": "A data binding reference to a location in the data model (e.g., '/user/name')." - }, - "literalString": { - "type": "string", - "description": "A fixed, hardcoded string value." - } - } - }, - "level": { - "type": "string", - "enum": [ - "1", - "2", - "3", - "4", - "5" - ], - "description": "The semantic importance level." - } - }, - "required": [ - "text" - ] - }, - "Text": { - "type": "object", - "properties": { - "text": { - "type": "object", - "description": "The text to display. This property supports markdown for rich text formatting. Can be a literal value or a data binding path. If both `path` and a `literal` value are provided, the client will initialize the data model at the given path with the literal value and then bind to that path.", - "properties": { - "path": { - "type": "string", - "description": "A data binding reference to a location in the data model (e.g., '/user/name')." - }, - "literalString": { - "type": "string", - "description": "A fixed, hardcoded string value. Markdown is supported." - } - } - } - }, - "required": [ - "text" - ] - }, - "Image": { - "type": "object", - "properties": { - "url": { - "type": "object", - "description": "The URL of the image to be displayed. Can be a literal value or a data binding path. If both `path` and a `literal` value are provided, the client will initialize the data model at the given path with the literal value and then bind to that path.", - "properties": { - "path": { - "type": "string", - "description": "A data binding reference to a location in the data model (e.g., '/user/name')." - }, - "literalString": { - "type": "string", - "description": "A fixed, hardcoded string value." - } - } - }, - "fit": { - "type": "string", - "enum": [ - "fill", - "cover", - "contain", - "none", - "scale-down" - ], - "default": "fill", - "description": "How the image should be inscribed into the box.\n- fill: Does not preserve the aspect ratio. The image is resized to fill the container (the image will be stretched or squeezed to fit).\n- cover: Preserves the aspect ratio, and the image fills the container. Cuts overflowing content if needed.\n- contain: Preserves the aspect ratio, and fits the image inside the container, without cutting - leaves empty space if needed.\n- none: The image is not resized.\n- scale-down: The image is scaled down to the smallest version of none or contain." - } - }, - "required": [ - "url" - ] - }, - "Video": { - "type": "object", - "properties": { - "url": { - "type": "object", - "description": "The URL of the video to be played. Can be a literal value or a data binding path. If both `path` and a `literal` value are provided, the client will initialize the data model at the given path with the literal value and then bind to that path.", - "properties": { - "path": { - "type": "string", - "description": "A data binding reference to a location in the data model (e.g., '/user/name')." - }, - "literalString": { - "type": "string", - "description": "A fixed, hardcoded string value." - } - } - } - }, - "required": [ - "url" - ] - }, - "AudioPlayer": { - "type": "object", - "properties": { - "url": { - "type": "object", - "description": "A description of the audio content, which can be used for accessibility or display purposes. Can be a literal value or a data binding path. If both `path` and a `literal` value are provided, the client will initialize the data model at the given path with the literal value and then bind to that path.", - "properties": { - "path": { - "type": "string", - "description": "A data binding reference to a location in the data model (e.g., '/user/name')." - }, - "literalString": { - "type": "string", - "description": "A fixed, hardcoded string value." - } - } - }, - "description": { - "type": "object", - "description": "The value for this property. Can be a literal value or a data binding path. If both `path` and a `literal` value are provided, the client will initialize the data model at the given path with the literal value and then bind to that path.", - "properties": { - "path": { - "type": "string", - "description": "A data binding reference to a location in the data model (e.g., '/user/name')." - }, - "literalString": { - "type": "string", - "description": "A fixed, hardcoded string value." - } - } - } - }, - "required": [ - "url" - ] - }, - "Row": { - "type": "object", - "properties": { - "children": { - "type": "object", - "description": "Defines the children of the container. You MUST define EITHER 'explicitList' OR 'template', but not both.", - "properties": { - "explicitList": { - "type": "array", - "description": "An explicit list of component instance IDs.", - "items": { - "type": "string", - "description": "A reference to a component instance by its unique ID." - } - }, - "template": { - "type": "object", - "description": "A template to be rendered for each item in a data-bound list.", - "properties": { - "componentId": { - "type": "string", - "description": "The ID of the component (from the main 'components' list) to use as a template for each item." - }, - "dataBinding": { - "type": "string", - "description": "A data binding reference to a list within the data model (e.g., '/user/posts')." - } - }, - "required": [ - "componentId", - "dataBinding" - ] - } - } - }, - "distribution": { - "type": "string", - "enum": [ - "start", - "center", - "end", - "spaceBetween", - "spaceAround", - "spaceEvenly" - ], - "description": "Distribution of items along the main axis." - }, - "alignment": { - "type": "string", - "enum": [ - "start", - "center", - "end", - "stretch" - ], - "description": "Alignment of items/child along the cross axis." - } - }, - "required": [ - "children" - ] - }, - "Column": { - "type": "object", - "properties": { - "children": { - "type": "object", - "description": "Defines the children of the container. You MUST define EITHER 'explicitList' OR 'template', but not both.", - "properties": { - "explicitList": { - "type": "array", - "description": "An explicit list of component instance IDs.", - "items": { - "type": "string", - "description": "A reference to a component instance by its unique ID." - } - }, - "template": { - "type": "object", - "description": "A template to be rendered for each item in a data-bound list.", - "properties": { - "componentId": { - "type": "string", - "description": "The ID of the component (from the main 'components' list) to use as a template for each item." - }, - "dataBinding": { - "type": "string", - "description": "A data binding reference to a list within the data model (e.g., '/user/posts')." - } - }, - "required": [ - "componentId", - "dataBinding" - ] - } - } - }, - "distribution": { - "type": "string", - "enum": [ - "start", - "center", - "end", - "spaceBetween", - "spaceAround", - "spaceEvenly" - ], - "description": "Distribution of items along the main axis." - }, - "alignment": { - "type": "string", - "enum": [ - "start", - "center", - "end", - "stretch" - ], - "description": "Alignment of items/child along the cross axis." - } - }, - "required": [ - "children" - ] - }, - "List": { - "type": "object", - "properties": { - "children": { - "type": "object", - "description": "Defines the children of the container. You MUST define EITHER 'explicitList' OR 'template', but not both.", - "properties": { - "explicitList": { - "type": "array", - "description": "An explicit list of component instance IDs.", - "items": { - "type": "string", - "description": "A reference to a component instance by its unique ID." - } - }, - "template": { - "type": "object", - "description": "A template to be rendered for each item in a data-bound list.", - "properties": { - "componentId": { - "type": "string", - "description": "The ID of the component (from the main 'components' list) to use as a template for each item." - }, - "dataBinding": { - "type": "string", - "description": "A data binding reference to a list within the data model (e.g., '/user/posts')." - } - }, - "required": [ - "componentId", - "dataBinding" - ] - } - } - }, - "direction": { - "type": "string", - "enum": [ - "vertical", - "horizontal" - ], - "default": "vertical", - "description": "The direction of the list." - }, - "alignment": { - "type": "string", - "enum": [ - "start", - "center", - "end", - "stretch" - ], - "description": "Alignment of items/child along the cross axis." - } - }, - "required": [ - "children" - ] - }, - "Card": { - "type": "object", - "properties": { - "child": { - "type": "string", - "description": "A reference to a single component instance by its unique ID." - } - }, - "required": [ - "child" - ] - }, - "Tabs": { - "type": "object", - "properties": { - "tabItems": { - "type": "array", - "description": "A list of tabs, each with a title and a child component ID.", - "items": { - "type": "object", - "properties": { - "title": { - "type": "object", - "description": "The title of the tab, which is displayed to the user. Can be a literal value or a data binding path. If both `path` and a `literal` value are provided, the client will initialize the data model at the given path with the literal value and then bind to that path.", - "properties": { - "path": { - "type": "string", - "description": "A data binding reference to a location in the data model (e.g., '/user/name')." - }, - "literalString": { - "type": "string", - "description": "A fixed, hardcoded string value." - } - } - }, - "child": { - "type": "string", - "description": "A reference to a component instance by its unique ID." - } - }, - "required": [ - "title", - "child" - ] - } - } - }, - "required": [ - "tabItems" - ] - }, - "Divider": { - "type": "object", - "properties": { - "axis": { - "type": "string", - "enum": [ - "horizontal", - "vertical" - ], - "default": "horizontal", - "description": "The orientation." - } - } - }, - "Modal": { - "type": "object", - "properties": { - "entryPointChild": { - "type": "string", - "description": "The ID of the component (e.g., a button) that triggers the modal." - }, - "contentChild": { - "type": "string", - "description": "The ID of the component to display as the modal's content." - } - }, - "required": [ - "entryPointChild", - "contentChild" - ] - }, - "Button": { - "type": "object", - "properties": { - "label": { - "type": "object", - "description": "The text label displayed on the button. Can be a literal value or a data binding path. If both `path` and a `literal` value are provided, the client will initialize the data model at the given path with the literal value and then bind to that path.", - "properties": { - "path": { - "type": "string", - "description": "A data binding reference to a location in the data model (e.g., '/user/name')." - }, - "literalString": { - "type": "string", - "description": "A fixed, hardcoded string value." - } - } - }, - "action": { - "type": "object", - "description": "Represents a user-initiated action.", - "properties": { - "action": { - "type": "string", - "description": "A unique name identifying the action (e.g., 'submitForm')." - }, - "context": { - "type": "array", - "description": "A key-value map of data bindings to be resolved when the action is triggered.", - "items": { - "type": "object", - "properties": { - "key": { - "type": "string" - }, - "value": { - "type": "object", - "description": "The dynamic value. Define EXACTLY ONE of the nested properties.", - "properties": { - "path": { - "type": "string", - "description": "A data binding reference to a location in the data model (e.g., '/user/name')." - }, - "literalString": { - "type": "string", - "description": "A fixed, hardcoded string value." - }, - "literalNumber": { - "type": "number" - }, - "literalBoolean": { - "type": "boolean" - } - } - } - }, - "required": [ - "key", - "value" - ] - } - } - }, - "required": [ - "action" - ] - } - }, - "required": [ - "label", - "action" - ] - }, - "CheckBox": { - "type": "object", - "properties": { - "label": { - "type": "object", - "description": "The label displayed next to the checkbox. Can be a literal value or a data binding path. If both `path` and a `literal` value are provided, the client will initialize the data model at the given path with the literal value and then bind to that path.", - "properties": { - "path": { - "type": "string", - "description": "A data binding reference to a location in the data model (e.g., '/user/name')." - }, - "literalString": { - "type": "string", - "description": "A fixed, hardcoded string value." - } - } - }, - "value": { - "type": "object", - "description": "The boolean state of the checkbox, where true means checked and false means unchecked. Can be a literal value or a data binding path. If both `path` and a `literal` value are provided, the client will initialize the data model at the given path with the literal value and then bind to that path.", - "properties": { - "path": { - "type": "string", - "description": "A data binding reference to a location in the data model (e.g., '/user/name')." - }, - "literalBoolean": { - "type": "boolean" - } - } - } - }, - "required": [ - "label", - "value" - ] - }, - "TextField": { - "type": "object", - "properties": { - "text": { - "type": "object", - "description": "The text content of the input field. Can be a literal value or a data binding path. If both `path` and a `literal` value are provided, the client will initialize the data model at the given path with the literal value and then bind to that path.", - "properties": { - "path": { - "type": "string", - "description": "A data binding reference to a location in the data model (e.g., '/user/name')." - }, - "literalString": { - "type": "string", - "description": "A fixed, hardcoded string value." - } - } - }, - "label": { - "type": "object", - "description": "The label for the text field, which is displayed to the user and should describe the expected input. Can be a literal value or a data binding path. If both `path` and a `literal` value are provided, the client will initialize the data model at the given path with the literal value and then bind to that path.", - "properties": { - "path": { - "type": "string", - "description": "A data binding reference to a location in the data model (e.g., '/user/name')." - }, - "literalString": { - "type": "string", - "description": "A fixed, hardcoded string value." - } - } - }, - "type": { - "type": "string", - "enum": [ - "shortText", - "number", - "date", - "longText" - ] - }, - "validationRegexp": { - "type": "string", - "description": "A regex string to validate the input." - } - }, - "required": [ - "label" - ] - }, - "DateTimeInput": { - "type": "object", - "properties": { - "value": { - "type": "object", - "description": "The selected date and/or time. The value must be provided in ISO 8601 format. Can be a literal value or a data binding path. If both `path` and a `literal` value are provided, the client will initialize the data model at the given path with the literal value and then bind to that path.", - "properties": { - "path": { - "type": "string", - "description": "A data binding reference to a location in the data model (e.g., '/user/name')." - }, - "literalString": { - "type": "string", - "description": "A fixed, hardcoded string value in ISO 8601 format." - } - } - }, - "enableDate": { - "type": "boolean", - "default": true - }, - "enableTime": { - "type": "boolean", - "default": false - }, - "outputFormat": { - "type": "string", - "description": "The string format for the output (e.g., 'YYYY-MM-DD')." - } - }, - "required": [ - "value" - ] - }, - "MultipleChoice": { - "type": "object", - "properties": { - "selections": { - "type": "object", - "description": "A list of strings representing the currently selected `value`s from the `options` list. Can be a literal value or a data binding path. If both `path` and a `literal` value are provided, the client will initialize the data model at the given path with the literal value and then bind to that path.", - "properties": { - "path": { - "type": "string", - "description": "A data binding reference to a location in the data model (e.g., '/user/name')." - }, - "literalArray": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "options": { - "type": "array", - "items": { - "type": "object", - "properties": { - "label": { - "type": "object", - "description": "The user-facing label for a choice in the list. Can be a literal value or a data binding path. If both `path` and a `literal` value are provided, the client will initialize the data model at the given path with the literal value and then bind to that path.", - "properties": { - "path": { - "type": "string", - "description": "A data binding reference to a location in the data model (e.g., '/user/name')." - }, - "literalString": { - "type": "string", - "description": "A fixed, hardcoded string value." - } - } - }, - "value": { - "type": "string" - } - }, - "required": [ - "label", - "value" - ] - } - }, - "maxAllowedSelections": { - "type": "integer", - "default": 1 - } - }, - "required": [ - "selections" - ] - }, - "Slider": { - "type": "object", - "properties": { - "value": { - "type": "object", - "description": "The current numerical value of the slider. Can be a literal value or a data binding path. If both `path` and a `literal` value are provided, the client will initialize the data model at the given path with the literal value and then bind to that path.", - "properties": { - "path": { - "type": "string", - "description": "A data binding reference to a location in the data model (e.g., '/user/name')." - }, - "literalNumber": { - "type": "number" - } - } - }, - "minValue": { - "type": "number", - "default": 0 - }, - "maxValue": { - "type": "number", - "default": 100 - } - }, - "required": [ - "value" - ] - } - }, - "styles": { - "font": { - "type": "string", - "description": "The primary font to be used throughout the UI." - }, - "logoUrl": { - "type": "string", - "description": "A URL pointing to the logo image to be displayed." - }, - "primaryColor": { - "type": "string", - "description": "The primary color for the UI, specified as a hexadecimal color code (e.g., '#00BFFF').", - "pattern": "^#[0-9a-fA-F]{6}$" - } - } -} diff --git a/specification/json/standard_catalog_description_llm.json b/specification/json/standard_catalog_description_llm.json deleted file mode 100644 index a4c9ba89..00000000 --- a/specification/json/standard_catalog_description_llm.json +++ /dev/null @@ -1,790 +0,0 @@ -{ - "title": "A2UI Protocol message", - "description": "A message from the server to the client, which can be one of several types.", - "properties": { - "surfaceId": { - "type": "string", - "description": "An ID for the surface that the UI changes should be applied to. If this surface doesn't exist, it will be created. If this is not specified, the default surface will be used." - }, - "beginRendering": { - "title": "BeginRendering Message", - "description": "A schema for a BeginRendering message in the A2A streaming UI protocol. This message signals that the UI can now be rendered and provides initial root component and styling information.", - "type": "object", - "properties": { - "root": { - "type": "string", - "description": "The ID of the root component from which rendering should begin. This is a reference to a component instance by its unique ID. This property is REQUIRED." - }, - "styles": { - "type": "object", - "description": "An object containing styling information for the UI.", - "properties": { - "font": { - "type": "string", - "description": "The primary font to be used throughout the UI." - }, - "logoUrl": { - "type": "string", - "description": "A URL pointing to the logo image to be displayed." - }, - "primaryColor": { - "type": "string", - "description": "The primary color for the UI, specified as a hexadecimal color code (e.g., '#00BFFF').", - "pattern": "^#[0-9a-fA-F]{6}$" - } - } - } - }, - "required": [ - "root" - ] - }, - "surfaceUpdate": { - "title": "SurfaceUpdate Message", - "description": "A schema for a SurfaceUpdate message in the A2A streaming UI protocol.", - "type": "object", - "properties": { - "components": { - "type": "array", - "description": "A flat list of all component instances available for rendering. Components reference each other by ID. This property is REQUIRED.", - "items": { - "description": "A specific instance of a ComponentType with its own unique ID and properties.", - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "A unique identifier for this component instance. This property is REQUIRED." - }, - "componentProperties": { - "type": "object", - "description": "Defines the properties for the component type.", - "properties": { - "Heading": { - "type": "object", - "properties": { - "text": { - "type": "object", - "description": "The value for this property. Can be a literal value or a data binding path. If both `path` and a `literal` value are provided, the client will initialize the data model at the given path with the literal value and then bind to that path.", - "properties": { - "path": { - "type": "string", - "description": "A data binding reference to a location in the data model (e.g., '/user/name')." - }, - "literalString": { - "type": "string", - "description": "A fixed, hardcoded string value." - } - } - }, - "level": { - "type": "string", - "enum": [ - "1", - "2", - "3", - "4", - "5" - ], - "description": "The semantic importance level." - } - }, - "required": [ - "text" - ] - }, - "Text": { - "type": "object", - "properties": { - "text": { - "type": "object", - "description": "The text to display. This property supports markdown for rich text formatting. Can be a literal value or a data binding path. If both `path` and a `literal` value are provided, the client will initialize the data model at the given path with the literal value and then bind to that path.", - "properties": { - "path": { - "type": "string", - "description": "A data binding reference to a location in the data model (e.g., '/user/name')." - }, - "literalString": { - "type": "string", - "description": "A fixed, hardcoded string value. Markdown is supported." - } - } - } - }, - "required": [ - "text" - ] - }, - "Image": { - "type": "object", - "properties": { - "url": { - "type": "object", - "description": "The URL of the image to be displayed. Can be a literal value or a data binding path. If both `path` and a `literal` value are provided, the client will initialize the data model at the given path with the literal value and then bind to that path.", - "properties": { - "path": { - "type": "string", - "description": "A data binding reference to a location in the data model (e.g., '/user/name')." - }, - "literalString": { - "type": "string", - "description": "A fixed, hardcoded string value." - } - } - }, - "fit": { - "type": "string", - "enum": [ - "fill", - "cover", - "contain", - "none", - "scale-down" - ], - "default": "fill", - "description": "How the image should be inscribed into the box.\n- fill: Does not preserve the aspect ratio. The image is resized to fill the container (the image will be stretched or squeezed to fit).\n- cover: Preserves the aspect ratio, and the image fills the container. Cuts overflowing content if needed.\n- contain: Preserves the aspect ratio, and fits the image inside the container, without cutting - leaves empty space if needed.\n- none: The image is not resized.\n- scale-down: The image is scaled down to the smallest version of none or contain." - } - }, - "required": [ - "url" - ] - }, - "Video": { - "type": "object", - "properties": { - "url": { - "type": "object", - "description": "The URL of the video to be played. Can be a literal value or a data binding path. If both `path` and a `literal` value are provided, the client will initialize the data model at the given path with the literal value and then bind to that path.", - "properties": { - "path": { - "type": "string", - "description": "A data binding reference to a location in the data model (e.g., '/user/name')." - }, - "literalString": { - "type": "string", - "description": "A fixed, hardcoded string value." - } - } - } - }, - "required": [ - "url" - ] - }, - "AudioPlayer": { - "type": "object", - "properties": { - "url": { - "type": "object", - "description": "A description of the audio content, which can be used for accessibility or display purposes. Can be a literal value or a data binding path. If both `path` and a `literal` value are provided, the client will initialize the data model at the given path with the literal value and then bind to that path.", - "properties": { - "path": { - "type": "string", - "description": "A data binding reference to a location in the data model (e.g., '/user/name')." - }, - "literalString": { - "type": "string", - "description": "A fixed, hardcoded string value." - } - } - }, - "description": { - "type": "object", - "description": "The value for this property. Can be a literal value or a data binding path. If both `path` and a `literal` value are provided, the client will initialize the data model at the given path with the literal value and then bind to that path.", - "properties": { - "path": { - "type": "string", - "description": "A data binding reference to a location in the data model (e.g., '/user/name')." - }, - "literalString": { - "type": "string", - "description": "A fixed, hardcoded string value." - } - } - } - }, - "required": [ - "url" - ] - }, - "Row": { - "type": "object", - "properties": { - "children": { - "type": "object", - "description": "Defines the children of the container. You MUST define EITHER 'explicitList' OR 'template', but not both.", - "properties": { - "explicitList": { - "type": "array", - "description": "An explicit list of component instance IDs.", - "items": { - "type": "string", - "description": "A reference to a component instance by its unique ID." - } - }, - "template": { - "type": "object", - "description": "A template to be rendered for each item in a data-bound list.", - "properties": { - "componentId": { - "type": "string", - "description": "The ID of the component (from the main 'components' list) to use as a template for each item." - }, - "dataBinding": { - "type": "string", - "description": "A data binding reference to a list within the data model (e.g., '/user/posts')." - } - }, - "required": [ - "componentId", - "dataBinding" - ] - } - } - }, - "distribution": { - "type": "string", - "enum": [ - "start", - "center", - "end", - "spaceBetween", - "spaceAround", - "spaceEvenly" - ], - "description": "Distribution of items along the main axis." - }, - "alignment": { - "type": "string", - "enum": [ - "start", - "center", - "end", - "stretch" - ], - "description": "Alignment of items/child along the cross axis." - } - }, - "required": [ - "children" - ] - }, - "Column": { - "type": "object", - "properties": { - "children": { - "type": "object", - "description": "Defines the children of the container. You MUST define EITHER 'explicitList' OR 'template', but not both.", - "properties": { - "explicitList": { - "type": "array", - "description": "An explicit list of component instance IDs.", - "items": { - "type": "string", - "description": "A reference to a component instance by its unique ID." - } - }, - "template": { - "type": "object", - "description": "A template to be rendered for each item in a data-bound list.", - "properties": { - "componentId": { - "type": "string", - "description": "The ID of the component (from the main 'components' list) to use as a template for each item." - }, - "dataBinding": { - "type": "string", - "description": "A data binding reference to a list within the data model (e.g., '/user/posts')." - } - }, - "required": [ - "componentId", - "dataBinding" - ] - } - } - }, - "distribution": { - "type": "string", - "enum": [ - "start", - "center", - "end", - "spaceBetween", - "spaceAround", - "spaceEvenly" - ], - "description": "Distribution of items along the main axis." - }, - "alignment": { - "type": "string", - "enum": [ - "start", - "center", - "end", - "stretch" - ], - "description": "Alignment of items/child along the cross axis." - } - }, - "required": [ - "children" - ] - }, - "List": { - "type": "object", - "properties": { - "children": { - "type": "object", - "description": "Defines the children of the container. You MUST define EITHER 'explicitList' OR 'template', but not both.", - "properties": { - "explicitList": { - "type": "array", - "description": "An explicit list of component instance IDs.", - "items": { - "type": "string", - "description": "A reference to a component instance by its unique ID." - } - }, - "template": { - "type": "object", - "description": "A template to be rendered for each item in a data-bound list.", - "properties": { - "componentId": { - "type": "string", - "description": "The ID of the component (from the main 'components' list) to use as a template for each item." - }, - "dataBinding": { - "type": "string", - "description": "A data binding reference to a list within the data model (e.g., '/user/posts')." - } - }, - "required": [ - "componentId", - "dataBinding" - ] - } - } - }, - "direction": { - "type": "string", - "enum": [ - "vertical", - "horizontal" - ], - "default": "vertical", - "description": "The direction of the list." - }, - "alignment": { - "type": "string", - "enum": [ - "start", - "center", - "end", - "stretch" - ], - "description": "Alignment of items/child along the cross axis." - } - }, - "required": [ - "children" - ] - }, - "Card": { - "type": "object", - "properties": { - "child": { - "type": "string", - "description": "A reference to a single component instance by its unique ID." - } - }, - "required": [ - "child" - ] - }, - "Tabs": { - "type": "object", - "properties": { - "tabItems": { - "type": "array", - "description": "A list of tabs, each with a title and a child component ID.", - "items": { - "type": "object", - "properties": { - "title": { - "type": "object", - "description": "The title of the tab, which is displayed to the user. Can be a literal value or a data binding path. If both `path` and a `literal` value are provided, the client will initialize the data model at the given path with the literal value and then bind to that path.", - "properties": { - "path": { - "type": "string", - "description": "A data binding reference to a location in the data model (e.g., '/user/name')." - }, - "literalString": { - "type": "string", - "description": "A fixed, hardcoded string value." - } - } - }, - "child": { - "type": "string", - "description": "A reference to a component instance by its unique ID." - } - }, - "required": [ - "title", - "child" - ] - } - } - }, - "required": [ - "tabItems" - ] - }, - "Divider": { - "type": "object", - "properties": { - "axis": { - "type": "string", - "enum": [ - "horizontal", - "vertical" - ], - "default": "horizontal", - "description": "The orientation." - } - } - }, - "Modal": { - "type": "object", - "properties": { - "entryPointChild": { - "type": "string", - "description": "The ID of the component (e.g., a button) that triggers the modal." - }, - "contentChild": { - "type": "string", - "description": "The ID of the component to display as the modal's content." - } - }, - "required": [ - "entryPointChild", - "contentChild" - ] - }, - "Button": { - "type": "object", - "properties": { - "label": { - "type": "object", - "description": "The text label displayed on the button. Can be a literal value or a data binding path. If both `path` and a `literal` value are provided, the client will initialize the data model at the given path with the literal value and then bind to that path.", - "properties": { - "path": { - "type": "string", - "description": "A data binding reference to a location in the data model (e.g., '/user/name')." - }, - "literalString": { - "type": "string", - "description": "A fixed, hardcoded string value." - } - } - }, - "action": { - "type": "object", - "description": "Represents a user-initiated action.", - "properties": { - "action": { - "type": "string", - "description": "A unique name identifying the action (e.g., 'submitForm')." - }, - "context": { - "type": "array", - "description": "A key-value map of data bindings to be resolved when the action is triggered.", - "items": { - "type": "object", - "properties": { - "key": { - "type": "string" - }, - "value": { - "type": "object", - "description": "The dynamic value. Define EXACTLY ONE of the nested properties.", - "properties": { - "path": { - "type": "string", - "description": "A data binding reference to a location in the data model (e.g., '/user/name')." - }, - "literalString": { - "type": "string", - "description": "A fixed, hardcoded string value." - }, - "literalNumber": { - "type": "number" - }, - "literalBoolean": { - "type": "boolean" - } - } - } - }, - "required": [ - "key", - "value" - ] - } - } - }, - "required": [ - "action" - ] - } - }, - "required": [ - "label", - "action" - ] - }, - "CheckBox": { - "type": "object", - "properties": { - "label": { - "type": "object", - "description": "The label displayed next to the checkbox. Can be a literal value or a data binding path. If both `path` and a `literal` value are provided, the client will initialize the data model at the given path with the literal value and then bind to that path.", - "properties": { - "path": { - "type": "string", - "description": "A data binding reference to a location in the data model (e.g., '/user/name')." - }, - "literalString": { - "type": "string", - "description": "A fixed, hardcoded string value." - } - } - }, - "value": { - "type": "object", - "description": "The boolean state of the checkbox, where true means checked and false means unchecked. Can be a literal value or a data binding path. If both `path` and a `literal` value are provided, the client will initialize the data model at the given path with the literal value and then bind to that path.", - "properties": { - "path": { - "type": "string", - "description": "A data binding reference to a location in the data model (e.g., '/user/name')." - }, - "literalBoolean": { - "type": "boolean" - } - } - } - }, - "required": [ - "label", - "value" - ] - }, - "TextField": { - "type": "object", - "properties": { - "text": { - "type": "object", - "description": "The text content of the input field. Can be a literal value or a data binding path. If both `path` and a `literal` value are provided, the client will initialize the data model at the given path with the literal value and then bind to that path.", - "properties": { - "path": { - "type": "string", - "description": "A data binding reference to a location in the data model (e.g., '/user/name')." - }, - "literalString": { - "type": "string", - "description": "A fixed, hardcoded string value." - } - } - }, - "label": { - "type": "object", - "description": "The label for the text field, which is displayed to the user and should describe the expected input. Can be a literal value or a data binding path. If both `path` and a `literal` value are provided, the client will initialize the data model at the given path with the literal value and then bind to that path.", - "properties": { - "path": { - "type": "string", - "description": "A data binding reference to a location in the data model (e.g., '/user/name')." - }, - "literalString": { - "type": "string", - "description": "A fixed, hardcoded string value." - } - } - }, - "type": { - "type": "string", - "enum": [ - "shortText", - "number", - "date", - "longText" - ] - }, - "validationRegexp": { - "type": "string", - "description": "A regex string to validate the input." - } - }, - "required": [ - "label" - ] - }, - "DateTimeInput": { - "type": "object", - "properties": { - "value": { - "type": "object", - "description": "The selected date and/or time. The value must be provided in ISO 8601 format. Can be a literal value or a data binding path. If both `path` and a `literal` value are provided, the client will initialize the data model at the given path with the literal value and then bind to that path.", - "properties": { - "path": { - "type": "string", - "description": "A data binding reference to a location in the data model (e.g., '/user/name')." - }, - "literalString": { - "type": "string", - "description": "A fixed, hardcoded string value in ISO 8601 format." - } - } - }, - "enableDate": { - "type": "boolean", - "default": true - }, - "enableTime": { - "type": "boolean", - "default": false - }, - "outputFormat": { - "type": "string", - "description": "The string format for the output (e.g., 'YYYY-MM-DD')." - } - }, - "required": [ - "value" - ] - }, - "MultipleChoice": { - "type": "object", - "properties": { - "selections": { - "type": "object", - "description": "A list of strings representing the currently selected `value`s from the `options` list. Can be a literal value or a data binding path. If both `path` and a `literal` value are provided, the client will initialize the data model at the given path with the literal value and then bind to that path.", - "properties": { - "path": { - "type": "string", - "description": "A data binding reference to a location in the data model (e.g., '/user/name')." - }, - "literalArray": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "options": { - "type": "array", - "items": { - "type": "object", - "properties": { - "label": { - "type": "object", - "description": "The user-facing label for a choice in the list. Can be a literal value or a data binding path. If both `path` and a `literal` value are provided, the client will initialize the data model at the given path with the literal value and then bind to that path.", - "properties": { - "path": { - "type": "string", - "description": "A data binding reference to a location in the data model (e.g., '/user/name')." - }, - "literalString": { - "type": "string", - "description": "A fixed, hardcoded string value." - } - } - }, - "value": { - "type": "string" - } - }, - "required": [ - "label", - "value" - ] - } - }, - "maxAllowedSelections": { - "type": "integer", - "default": 1 - } - }, - "required": [ - "selections" - ] - }, - "Slider": { - "type": "object", - "properties": { - "value": { - "type": "object", - "description": "The current numerical value of the slider. Can be a literal value or a data binding path. If both `path` and a `literal` value are provided, the client will initialize the data model at the given path with the literal value and then bind to that path.", - "properties": { - "path": { - "type": "string", - "description": "A data binding reference to a location in the data model (e.g., '/user/name')." - }, - "literalNumber": { - "type": "number" - } - } - }, - "minValue": { - "type": "number", - "default": 0 - }, - "maxValue": { - "type": "number", - "default": 100 - } - }, - "required": [ - "value" - ] - } - } - } - }, - "required": [ - "id", - "componentProperties" - ] - } - } - }, - "required": [ - "components" - ] - }, - "dataModelUpdate": { - "title": "Data model update", - "description": "Sets or replaces the data model at a specified path with new content.", - "type": "object", - "properties": { - "path": { - "type": "string", - "description": "An optional path to a location within the data model where the content should be inserted or replaced. The path is represented as a dot-separated string and can include array indexing (e.g., 'user.addresses[0].street'). If this field is omitted, the entire data model will be replaced with the provided 'contents'." - }, - "contents": { - "description": "The JSON content to be placed at the specified path. This property is REQUIRED. This can be any valid JSON value (object, array, string, number, boolean, or null). The content at the target path will be completely replaced by this new value." - } - }, - "required": [ - "contents" - ] - }, - "surfaceDeletion": { - "title": "SurfaceDeletion Message", - "description": "A schema for a SurfaceDeletion message in the A2A streaming UI protocol. This message signals that a surface should be removed from the UI.", - "type": "object", - "properties": {}, - "required": [] - } - } -}